各位大佬们大家好😊,很久没更新文章了。因为前端时间挺忙的,各种项目、准备软考等等给耽搁了🙏。最近这段时间有点迷上threeJs,经过搜寻发现网络上关于threeJs的资源还是相对较少的,而且比较旧。
今天我使用threeJs来实现一个全景视频,带领大家观看梅赛德斯的科技展览🎆,效果大概如下:
在开始之前,您必须先了解threeJs的知识,这里为了节省篇幅,就不做过多的说明,有关threeJs入门的只是各位可以查看以下链接:
下面我们开始进入正题
资源准备
我们进入threeJs github地址下载 three.min.js 和 OrbitControls.js,这两个我们等下会用到,其次需要准备一个 360全景视频 ,还有一些小图标之类的。视频下载地址我已经忘了,不过不用担心,上面提到的所有资源我都会放在源码中提供给大家
正式编码
准备雏形
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| // html <div class="big-container"> <div class="screen-container" id="screenContainer"> <div class="video-container" id="videoContainer"></div> </div> </div>
// style * { margin: 0; padding: 0; } body { position: relative; width: 100vw; height: 100vh; background-color: #666565; } .big-container { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90vw; height: 80vh; } .screen-container { width: 100%; height: 100%; } .video-container { width: 100%; height: 100%; background-color: #000000; }
|
编码完成后看到如此界面
准备3d环境
将本次demo中所需的两个文件引入进来,编写一些初始化函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| <script src="./js/three.min.js"></script> <script src="./js/OrbitControls.js"></script>
<script> // 初始化一些参数 var scene = null; var camera = null; var renderer = null; var controls = null; var video = null; var mesh = null; var player = null; var playVariables = { playClick: true, isBigScreen: false, }; // 获取容器 var videoContainer = document.getElementById("videoContainer"); initScene(); initCamera(videoContainer); initRenderer(videoContainer); initControls(videoContainer); render(); // 初始化场景 function initScene() { scene = new THREE.Scene(); } // 初始化相机 function initCamera(element) { camera = new THREE.PerspectiveCamera( 75, element.clientWidth / element.clientHeight, 1, 1100 ); camera.position.set(1, 0, 0); } // 初始化渲染器 function initRenderer(element) { renderer = new THREE.WebGLRenderer(); renderer.setSize(element.offsetWidth, element.offsetHeight); element.appendChild(renderer.domElement); } // 初始化控制器 function initControls(element) { controls = new THREE.OrbitControls(camera, element); controls.rotateSpeed = 0.05; controls.enableDamping = true; controls.dampingFactor = 0.05; } // 执行渲染 function render() { requestAnimationFrame(render); controls.update(); renderer.render(scene, camera); } </script>
|
执行完上面代码,还是看不到效果的,我们只需要保证不报错即可
初始化视频并将视频渲染到环境中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| initVideo(); initContent();
// 创建video标签并配置一些属性、视频地址等,将video赋值给player方便后面控制用 function initVideo() { video = document.createElement("video"); video.preload = "auto"; video.crossOrigin = "anonymous"; video.src = "./resources/video.mp4"; player = video; } // 创建一个球型几何体并将视频作为纹理贴图贴到材质中,最后生成网格模型并添加到场景里 function initContent() { var geometry = new THREE.SphereBufferGeometry(300, 90, 90); geometry.scale(-1, 1, 1); var texture = new THREE.VideoTexture(video); texture.minFilter = THREE.LinearFilter; texture.format = THREE.RGBFormat; var material = new THREE.MeshBasicMaterial({ map: texture, }); mesh = new THREE.Mesh(geometry, material); mesh.position.set(0, 0, 0); scene.add(mesh); }
|
处理完后依旧是漆黑一片,但是可以通过video的autoplay属性让视频播放起来(但是我是用是只在服务器热更新时有效果,可能我没有等待视频加载完成🐶)
准备控制按钮让视频播放起来
在这里我写了一个全屏遮罩
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| // html <div class="video-mask"> <img src="./resources/play.png" onClick="toggleVideo()" alt="play.png" /> </div>
// style .video-mask { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.8); } .video-mask img { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 10%; }
// javascript // playClick是播放状态 false - 未播放 true - 正在播放 function toggleVideo() { if (playVariables.playClick) { document.getElementsByClassName("video-mask")[0].style.display = "none"; playVariables.playClick = false; player.play(); } else { playVariables.playClick = true; player.pause(); } }
|
我们来看下当前的效果,如下图所示,我们已经成功制作出全景视频了

加入底部控制栏
其实我们上面已经将全景视频制作出来,该操作也同样应用于全景直播。细想,接入视频流,转为贴图贴到几何模型,再添加到场景中,最后渲染出来,配上控制器,这不是一摸一样的效果吗😜
现在我们为了让上面视频操作更加完善,我们加入底部控制按钮,播放/暂停、进度条、全屏、窗口重置大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
| // html <div class="video-bar"> <div class="video-btn" onclick="toggleVideo()"> <img src="./resources/pause.png" class="btn-hidden" alt="pause.png" width="68%" /> <img src="./resources/play.png" alt="pause.png" width="68%" /> </div> <div class="progress-container"> <div class="time-box"> <span class="current-video-time">00:00</span>/<span class="total-video-time">00:00</span> </div> <div class="progress-wrapper"> <div class="progress" id="progress-play"></div> </div> </div> <div class="full-screen" onclick="toggleBigScreen()"> <img src="./resources/bigscreen.png" alt="bigscreen.png" width="68%" /> </div> </div>
// 这段style太长了 我就不贴上来了,详细可查看源码
// javascript // 处理toggleVideo函数 function toggleVideo() { var imgArr = document.getElementsByClassName("video-btn")[0].children; if (playVariables.playClick) { imgArr[1].setAttribute("class", "btn-hidden"); imgArr[0].setAttribute("class", ""); document.getElementsByClassName("video-mask")[0].style.display = "none"; playVariables.playClick = false; player.play(); } else { imgArr[0].setAttribute("class", "btn-hidden"); imgArr[1].setAttribute("class", ""); playVariables.playClick = true; player.pause(); } }
// 进度条计算以及时间数值 // 在initVideo中添加监听函数 // video可以用ontimeupdate来获取视频总长度和当前播放位置 // s_to_hs函数是秒转时分秒函数,百度可以找到,这里不贴上来,文末源码自取 function initVideo() { video = document.createElement("video"); video.preload = "auto"; video.crossOrigin = "anonymous"; video.src = "./resources/video.mp4"; player = video;
video.ontimeupdate = function (event) { var totalTime = s_to_hs(Math.floor(this.duration)); document.getElementsByClassName("total-video-time")[0].innerHTML = totalTime;
var currentTime = s_to_hs(Math.floor(this.currentTime)); document.getElementsByClassName("current-video-time")[0].innerHTML = currentTime;
document.getElementById("progress-play").style.width = `${ (Math.floor(this.currentTime) / Math.floor(this.duration)) * 100 }%`; // 视频播放完毕时回到最开始的状态 if (currentTime === totalTime) { video.currentTime = 0; toggleVideo(); document.getElementsByClassName("video-mask")[0].style.display = "block"; } }; }
// 点击进度条,视频前进到对应的位置(这边算的可能有些不准,所以操作起来怪怪的) var progressContainer = document.getElementsByClassName("progress-wrapper")[0]; progressContainer.addEventListener("click", function (event) { var progressWidth = document.getElementById("screenContainer").offsetWidth - 248; var percentage = event.offsetX / progressWidth; video.currentTime = video.duration * percentage; });
// 全屏处理 function toggleBigScreen() { if (playVariables.isBigScreen) { exitFullscreen(); } else { requestFullScreen(); } } // 进入全屏,更改状态 function requestFullScreen() { var de = document.getElementById("screenContainer"); if (de.requestFullscreen) { de.requestFullscreen(); } else if (de.mozRequestFullScreen) { de.mozRequestFullScreen(); } else if (de.webkitRequestFullScreen) { de.webkitRequestFullScreen(); } playVariables.isBigScreen = true; } // 推出全屏更改状态 function exitFullscreen() { var de = document; if (de.exitFullscreen) { de.exitFullscreen(); } else if (de.mozCancelFullScreen) { de.mozCancelFullScreen(); } else if (de.webkitCancelFullScreen) { de.webkitCancelFullScreen(); } playVariables.isBigScreen = false; } // 监听resize,重置场景大小 window.addEventListener("resize", function () { camera.aspect = videoContainer.clientWidth / videoContainer.clientHeight; camera.updateProjectionMatrix(); renderer.setSize( videoContainer.offsetWidth, videoContainer.offsetHeight ); }, false);
|
让我们来看看最终效果

源码地址
因为github不能上传大于100M的文件(原4K视频580M),所以演示项目中的视频我压缩了4次,把他压缩到了62M,因此画质可能有点糊了,但不影响使用。
有条件的同学可以自己去寻找 360全景视频 或更换在线的视频连接,下面是本次教程的源码和视频资源下载网站: