工具栏-VR效果代码展示:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> <title>工具栏-VR效果</title> <script src="./js/three.js"></script> <script src="./js/OBJLoader.js"></script> <script src="./js/OrbitControls.js"></script> <script src="./js/StereoEffect.js"></script> <style> /* 初始化页面 */ * { margin: 0; padding: 0; box-sizing: border-box; } /* 设置渲染容器的大小 */ #webgl { width: 100%; height: 100vh; overflow: hidden; } /* 设置最大元素宽高 */ .content{ width: 100%; height: 100vh; position: relative; } /* 将工具栏定位到页面右下角 */ .tool{ position: absolute; bottom: 0; right: 0; margin-right: 20px; display: flex; user-select: none; } /* 设置每一个块的大小 */ .tool-item{ width: 35px; height: 35px; padding: 5px; position: relative; } /* 设置小图标黑色背景 */ .tool-img{ display: flex; justify-content: center; align-items: center; width: 25px; height: 25px; border-radius: 100%; background-color: rgba(0,0,0,0.8); } /* 设置小图标大小 */ .tool-img img{ width: 13px; height: 13px; } /* 设置小图标的提示文本 */ .tool-text{ display: block; line-height: 20px; padding: 6px 0px; border-radius: 5px; background-color: #000000; font-size: 12px; text-align: center; color: #FFFFFF; position: absolute; bottom: 40px; left: 50%; display: none; } /* 设置提示文本下边的小三角 */ .tool-item i{ display: none; width: 10px; height: 10px; background-color: #000000; position: absolute; bottom: 36px; left: 50%; margin-left: -5px; transform: rotate(45deg); } /* 鼠标放到每一块上显示提示文本 */ .tool-item:hover .tool-text{ display: block; } /* 鼠标放到每一块上显示小三角 */ .tool-item:hover i{ display: block; } /* 设置查看弹窗大小和位置 */ .popout{ width: 160px; position: absolute; bottom: 40px; background-color: #000000; padding: 6px 0; border-radius: 10px; font-size: 12px; color: #FFFFFF; right: -30px; z-index: 100; } /* 设置查看弹窗下边的小三角 */ .popout-arrow{ display: block; width: 10px; height: 10px; background-color: #000000; position: absolute; bottom: 36px; left: 50%; margin-left: -5px; transform: rotate(45deg); } /* 设置弹窗中每行的大小 */ .popout p{ width: 90%; height: 30px; margin-left: 5%; display: flex; justify-content: space-between; line-height: 30px; } /* 设置弹窗中每行箭头的大小 */ .popout>p img{ width: 12px; height: 12px; margin: 9px 0px; } /* 设置弹窗中的分界线 */ .popout em{ display: block; width: 100%; height: 1px; background-color: #525252; } /* 设置弹窗中背景图的那一行 */ .popout-list{ width: 90%; height: 30px; margin-left: 5%; padding: 5px 0; display: flex; } /* 设置背景图中左右按钮的大小 */ .popout-list>img{ width: 14px; height: 14px; margin: 3px; } /* 设置背景图列表 */ .popout-pic{ display: flex; justify-content: space-between; width: calc(100% - 40px); } /* 设置背景图大小 */ .popout-pic img{ width: 20px; height: 20px; } /* 设置帮助弹窗 */ .help{ width: 100%; height: 100%; position: absolute; top: 0; z-index: 999; background-color: rgba(0,0,0,0.9); display: none; } /* 设置帮助弹窗标题 */ .help h3{ width: 100%; line-height: 50px; text-align: center; color: #FFFFFF; font-weight: 500; } /* 设置帮助弹窗关闭按钮 */ .help-pic{ width: 20px; height: 20px; margin: 10px; position: absolute; top: 0; right: 0; } /* 设置帮助弹窗提示列表 */ .help-hint{ width: 100%; display: flex; justify-content: space-around; } /* 设置每项提示的大小 */ .help-item{ width: 200px; display: flex; justify-content: center; flex-wrap: wrap; } /* 设置每项提示的图标 */ .help-item img{ width: 20px; height: 20px; margin-bottom: 5px; } /* 设置每项提示的文字 */ .help-hint p{ width: 100%; list-style: 30px; color: #FFFFFF; text-align: center; font-size: 14px; } /* 设置重置相机按钮 */ .help-button{ width: 150px; height: 30px; border-radius: 5px; background-color: #FFFFFF; color: #2489F3; line-height: 30px; text-align: center; position: absolute; bottom: 10px; left: 50%; margin-left: -100px; } </style> </head> <body> <div class="content"> <!-- 渲染容器 --> <div id="webgl"></div> <!-- 工具栏 --> <div class="tool"> <div class="tool-item"> <span class="tool-text" id="rotate-text" style="width: 70px;margin-left: -35px;">开启旋转</span> <i></i> <div class="tool-img" onclick="rotates()"> <img src="./img/旋转.png" alt=""> </div> </div> <div class="tool-item"> <span class="tool-text" id="AR-text" style="width: 70px;margin-left: -35px;">开启AR</span> <i></i> <div class="tool-img" onclick="ARs()"> <img src="./img/AR.png" alt=""> </div> </div> <div class="tool-item"> <span class="tool-text" style="width: 50px;margin-left: -25px;">帮助</span> <i></i> <div class="tool-img" onclick="helpShow()"> <img src="./img/帮助.png" alt=""> </div> </div> <div class="tool-item"> <span class="tool-text" style="width: 70px;margin-left: -35px;">查看方式</span> <i></i> <div onclick="look()" class="tool-img"> <img src="./img/查看.png" alt=""> </div> <!-- 查看弹窗 --> <div class="popout" style="display: none;"> <p onclick="unfold(event)"><span>展示模式</span><img src="img/展开.png" alt=""></p> <p onclick="zoom()" style="padding-left: 10px;display: none;"><span>允许缩放</span><img class="zoomImg" src="./img/对.png" alt=""></p> <em></em> <p onclick="unfold(event)"><span>背景切换</span><img src="./img/展开.png" alt=""></p> <div class="popout-list" style="display: none;"> <img onclick="picLeft()" src="./img/左箭头.png" alt=""> <div class="popout-pic"></div> <img onclick="picRight()" src="./img/右箭头.png" alt=""> </div> </div> <b class="popout-arrow" style="display: none;"></b> </div> <div class="tool-item"> <span class="tool-text" id="VR-text" style="width: 70px;margin-left: -35px;">开启VR</span> <i></i> <div class="tool-img" onclick="openVR()"> <img src="./img/VR.png" alt=""> </div> </div> <div class="tool-item"> <span class="tool-text" style="width: 70px;margin-left: -35px;">开启全屏</span> <i></i> <div class="tool-img"> <img src="./img/全屏.png" alt=""> </div> </div> </div> <!-- 帮助弹窗 --> <div class="help"> <h3>操作导航</h3> <img class="help-pic" src="./img/错.png" alt="" onclick="helpHide()"> <div class="help-hint"> <div class="help-item"> <img src="./img/拖动.png" alt=""> <p style="margin-bottom: 10px;">旋转</p> <p>单击 + 拖动</p> <p>或一根手指拖动</p> </div> <div class="help-item"> <img src="./img/变焦.png" alt=""> <p style="margin-bottom: 10px;">变焦</p> <p>滚轮或两根手指</p> <p>向内或向外分开</p> </div> <div class="help-item"> <img src="./img/移动.png" alt=""> <p style="margin-bottom: 10px;">移动</p> <p>右击 + 拖动</p> <p>或三根手指拖动</p> </div> </div> <div class="help-button" onclick="helpReset()">重置相机视图</div> </div> </div> <script> // 获取渲染容器 let webgl = document.getElementById("webgl"); // 获取渲染容器的宽高 let webglWidth = webgl.offsetWidth; let webglHeight = webgl.offsetHeight; // 创建场景 let scene = new THREE.Scene(); // 设置环境光(十六进制颜色) let ambient = new THREE.AmbientLight(0x444444); // 将环境光添加到场景中 scene.add(ambient); // 设置点光源(十六进制颜色) let point = new THREE.PointLight(0xffffff); // 设置点光源的位置(x轴, y轴, z轴) point.position.set(400, 200, 300); // 将点光源添加到场景中 scene.add(point); // 创建透视相机(角度, 宽高比, 最近距离, 最远距离) let camera = new THREE.PerspectiveCamera(60,webglWidth/webglHeight,0.1,2000); // 设置相机的位置(x轴, y轴, z轴) camera.position.set(100, 100, 100); // 将相机指向场景中心 camera.lookAt(scene.position); // 创建渲染器 let renderer = new THREE.WebGLRenderer(); // 设置渲染器的初始颜色(十六进制颜色, 透明度) renderer.setClearColor(0xEEEEEE,1); // 设置渲染器大小(标签宽度, 标签高度) renderer.setSize(webglWidth,webglHeight); // 将渲染器添加到渲染容器中(渲染器元素) webgl.appendChild(renderer.domElement); // 创建旋转状态 let rotateStatus = false; // 创建 VR 渲染 let VR = new THREE.StereoEffect(renderer); // 创建 VR 开启状态,默认 false 表示不开启 let VRStatus = false; // 创建渲染函数 function render(){ // 判断旋转状态为 ture 则执行 if(rotateStatus){ // 设置场景旋转速度 scene.rotation.y += 0.002; } // 判断 VR 开启状态为 true 则开启 if(VRStatus){ // 设置 VR 渲染器的宽高 VR.setSize(webglWidth,webglHeight); // 通过 VR 渲染场景和相机 VR.render(scene, camera); }else{ // 渲染场景和相机(场景, 相机) renderer.render(scene,camera); } // 重复执行渲染函数 requestAnimationFrame(render); } // 调用渲染函数 render(); // 设置窗口变化自适应调整事件 window.onresize = function(){ // 重新获取渲染容器的宽高 webglWidth = webgl.offsetWidth; webglHeight = webgl.offsetHeight; // 重置渲染器canvas画布大小 renderer.setSize(webglWidth,webglHeight); // 重置相机显示范围的宽高比 camera.aspect = webglWidth/webglHeight; // 更新相机的投影矩阵 camera.updateProjectionMatrix(); // 重新调用渲染函数 render(); }; // 创建 OBJ 模型加载器 let loader = new THREE.OBJLoader(); // 加载 OBJ 文件 loader.load('./img/鹤.OBJ', function(obj) { // 加载纹理贴图 let texture = new THREE.TextureLoader().load('./img/he.png',function(){ render(); // 加载成功后重新调用渲染函数 }); // 给 OBJ 模型设置纹理贴图 obj.children[0].material = new THREE.MeshBasicMaterial({ map: texture }); // 将 OBJ 模型添加到场景中 scene.add(obj); // 设置 OBJ 模型居中 obj.children[0].geometry.center(); // 设置 OBJ 模型缩放大小 obj.children[0].scale.set(100, 100, 100); }) // 鼠标操作三维场景 let controls = new THREE.OrbitControls(camera,renderer.domElement); // 查看弹窗显示隐藏事件 function look(){ // 获取查看弹窗 let popout = document.getElementsByClassName("popout")[0]; // 获取查看弹窗下边的小三角 let arrow = document.getElementsByClassName("popout-arrow")[0]; // 判断弹窗是否隐藏 if(popout.style.display == "none"){ // 如果隐藏,显示弹窗和小三角 popout.style.display = "block"; arrow.style.display = "block"; }else{ // 如果显示,则隐藏弹窗和小三角 popout.style.display = "none"; arrow.style.display = "none"; } } // 查看弹窗功能展开事件 function unfold(e){ // 获取事件目标对象 e = e || window.event; let targets = e.target || e.srcElement; // 创建变量用于存储下一个同级元素 let nexts; // 创建变量用于存储当前 p 标签内的 img 标签 let pImg; // 当前目标元素是否为 p 标签 if(targets.nodeName == "P"){ // 如果为p标签,存储当前目标元素的下一个同级元素 nexts = targets.nextElementSibling; // 存储当前 p 标签内的 img 标签 pImg = targets.getElementsByTagName("img")[0]; }else{ // 如果不是p标签,存储当前目标元素的父元素的下一个同级元素 nexts = targets.parentElement.nextElementSibling; // 存储当前目标元素的父元素内的 img 标签 pImg = targets.parentElement.getElementsByTagName("img")[0]; } // 判断下一个同级元素是否隐藏 if(nexts.style.display == "none"){ // 如果隐藏则显示 nexts.style.display = "flex"; // 设置小图标箭头朝下 pImg.style.transform = "rotate(90deg)"; }else{ // 如果显示则隐藏 nexts.style.display = "none"; // 设置小图标箭头朝右 pImg.style.transform = "rotate(0deg)"; } } // 模拟背景图片数据 // 设置状态 1 表示正常图片,2 表示 3D 背景图 let arr = [ {img:"./img/pic1.jpg",status:1}, {img:"./img/pic2.webp",status:1}, {img:"./img/pic3.webp",status:1}, {img:"./img/pic4.webp",status:1}, {img:"./img/pic5.jpg",status:2}, ]; // 输出背景图数据 // 创建起始值,表示从第几张图片开始输出 let begin = 0; // 创建结束值,表示到第几张图片输出结束 // 并且判断图片数量是否大于等于4,如果大于设置为 4,否则设置为图片的数量 let record = arr.length >= 4 ? 4 : arr.length; // 创建背景图渲染函数 function picRender(){ // 创建变量,存储拼接的字符串 let picStr = ""; // 循环拼接数据,从 begin 开始,到 record 结束 for(let i = begin; i < record; i++){ picStr += `<img src="${arr[i].img}" alt="${arr[i].status}" >`; } // 向页面输出数据 document.getElementsByClassName("popout-pic")[0].innerHTML = picStr; } // 调用背景图渲染函数 picRender(); // 背景图切换-左按钮事件 function picLeft(){ // 判断起始值是否大于 0 if(begin > 0){ // 如果大于 0 让 起始值 和 结束值 都减一 record--; begin--; // 重新调用背景图渲染函数 picRender(); } } // 背景图切换-右按钮事件 function picRight(){ // 判断图片数量减起始值是否大于 4 if(arr.length - begin > 4){ // 如果大于 4 让 起始值 和 结束值 都加一 record++; begin++; // 重新调用背景图渲染函数 picRender(); } } // 创建模型旋转点击事件 function rotates(){ // 获取旋转提问文本 let rotateText = document.getElementById("rotate-text"); // 取反旋转状态 rotateStatus = !rotateStatus; // 判断旋转状态是否为 true if(rotateStatus){ // 如果为 true,设置提示文本为停止旋转 rotateText.innerHTML = "停止旋转"; }else{ // 如果为 false,设置提示文本为开启旋转 rotateText.innerHTML = "开启旋转"; } } // 创建 AR 状态 let ARStatus = false; // 创建 AR 开关事件 function ARs(){ // 获取 AR 提示文本 let ARText = document.getElementById("AR-text"); // 取反 AR 状态 ARStatus = !ARStatus; // 判断 AR 状态是否为 true if(ARStatus){ // 清除背景图 scene.background = ""; // 删除球几何体 scene.remove(scene.getObjectByName("ball")); // 如果为 true 设置渲染器背景色为黑色 renderer.setClearColor(0x000000,1); // 设置提示文本为关闭AR ARText.innerHTML = "关闭AR"; }else{ // 加载背景图 let texture = new THREE.TextureLoader().load(arr[0].img); // 设置背景图 scene.background = texture; // 否则设置渲染器背景色为灰色 renderer.setClearColor(0xEEEEEE,1); // 设置提示文本为开启AR ARText.innerHTML = "开启AR"; } } // 显示帮助弹窗 function helpShow(){ // 获取帮助弹窗并显示 let help = document.getElementsByClassName("help")[0]; help.style.display = "block"; } // 隐藏帮助弹窗 function helpHide(){ // 获取帮助弹窗并隐藏 let help = document.getElementsByClassName("help")[0]; help.style.display = "none"; } // 重置相机视角 function helpReset() { // 获取帮助弹窗并隐藏 let help = document.getElementsByClassName("help")[0]; help.style.display = "none"; // 重置相机的位置(x轴, y轴, z轴) camera.position.set(100, 100, 100); // 重置控制器目标位置 controls.target.set(0, 0, 0); // 更新控制器 controls.update(); // 重新调用渲染函数 render(); } // 创建禁止缩放状态 let zoomStatus = false; // 设置是否禁止缩放事件 function zoom(){ // 先获取禁止缩放中的小图标 let zoomImg = document.getElementsByClassName("zoomImg")[0]; // 取反禁止缩放状态 zoomStatus = !zoomStatus; // 取反后判断禁止缩放状态是否为 true if(zoomStatus){ // 如果为 true,设置控制器禁止缩放 controls.enableZoom = false; // 并修改图片为 X 的小图标 zoomImg.src = "./img/错.png"; }else{ // 否则设置控制器开启缩放 controls.enableZoom = true; // 并修改图片为 √ 的小图标 zoomImg.src = "./img/对.png"; } } // 加载默认背景图 let texture = new THREE.TextureLoader().load(arr[0].img); // 设置背景图 scene.background = texture; // 获取背景图容器 let backPic = document.getElementsByClassName("popout-pic")[0]; // 给背景图容器绑定事件委托 backPic.addEventListener("click",function(e){ // 获取事件源 e = e || window.event; let targets = e.target || e.srcElement; // 判断事件源是否 img 标签 if(targets.nodeName == "IMG"){ // 判断是否 3D 图片,1 表示普通图片,2 表示 3D 图片 if(targets.alt == 1){ // 重新加载背景图 texture = new THREE.TextureLoader().load(targets.src); // 设置背景图 scene.background = texture; // 删除球几何体 scene.remove(scene.getObjectByName("ball")); // 设置禁止缩放状态为 false zoomStatus = false; // 设置控制器开启缩放 controls.enableZoom = true; }else{ // 清除背景图 scene.background = ""; // 删除球几何体 scene.remove(scene.getObjectByName("ball")); // 创建球体作为 3D 背景使用(半径,分段数,分段数) let geometry = new THREE.SphereGeometry(500, 60, 60); // 重新加载背景图 texture = new THREE.TextureLoader().load(targets.src); // 将背景图设置为球体的材质 let material = new THREE.MeshBasicMaterial({ map: texture }); // 创建球几何体 let sphere = new THREE.Mesh(geometry, material); // 设置球几何体的名称 sphere.name = "ball"; // 设置球几何体的大小 sphere.geometry.scale(-1, 1, 1); // 将球几何体添加到场景中 scene.add(sphere); // 调用重置相机方法 helpReset(); // 设置禁止缩放状态为 true zoomStatus = true; // 设置控制器禁止缩放 controls.enableZoom = false; } } }) // 创建 VR 开关事件 function openVR(){ // 获取 VR 提示文本 let VRText = document.getElementById("VR-text"); // 取反 VR 开启状态 VRStatus = !VRStatus; // 判断 VR 开启状态是否为 true if(VRStatus){ // 设置 VR 提示文本为 关闭VR VRText.innerHTML = "关闭VR"; }else{ // 设置 VR 提示文本为 开启VR VRText.innerHTML = "开启VR"; // 重新设置渲染器的宽高 renderer.setViewport(0, 0, webglWidth, webglHeight); } } </script> </body> </html>
工具栏-VR显示效果:
原创作者:吴小糖
创作时间:2022.11.28