Hello everyone 😊, long time no update of the article. Because the front time is very busy, various projects, preparing for the soft test and so on to delay 🙏. Recently this period of time a little obsessed with threeJs, through the search found that the network about threeJs resources are relatively few, and relatively old.

Today I’m using threeJs to create a panoramic video to take you through the Mercedes technology exhibition 🎆, which looks something like this:

Before you can get started, you need to know about threeJs. To save space, I won’t go into too much detail here. Here’s how to get started with threeJs:

  • ThreeJs official English document https://threejs.org/
  • Guo Longbang technology blog http://www.yanhuangxueyuan.com/
  • ThreeJs making address https://github.com/mrdoob/three.js/

Now let’s get down to business

Resources to prepare

We go to threeJs Github to download three.min.js and OrbitControls.js, which we will use later, and then we need to prepare a 360 panoramic video and some small ICONS and so on. I’ve forgotten where to download the video, but don’t worry, all the resources mentioned above will be provided to you in the source code

Formal code

To prepare a prototype

// 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;
}
Copy the code

After coding, you see something like this

Preparing for 3d Environments

Bring in the two files needed for this demo and write some initialization functions

<script SRC ="./js/three.min.js"></script> <script SRC ="./js/ orbitcontrols.js "></script> <script> // Initialize some parameters 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 (); Camera = new THREE.PerspectiveCamera(75, PerspectiveCamera); 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>Copy the code

After executing the above code, we still can not see the effect, we just need to ensure that no error can be reported

Initialize the video and render it into the environment

initVideo(); initContent(); Function initVideo() {video = document.createElement("video"); video.preload = "auto"; video.crossOrigin = "anonymous"; video.src = "./resources/video.mp4"; player = video; } // Create a sphere geometry and paste the video into the material as a texture map, Finally to generate mesh model and the function is added to the scene 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); }Copy the code

It is still dark after processing, but the autoplay property of the video can make the video play (but I only use it when the server has hot update, maybe I did not wait for the video to finish loading 🐶).

Prepare the control button for the video to play

I’ve put a full screen mask here

// 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: rgba(0, 0, 0, 0.8); } .video-mask img { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 10%; Function toggleVideo() {if (playvariable.playclick) {function toggleVideo() {if (playvariable.playclick) { document.getElementsByClassName("video-mask")[0].style.display = "none"; playVariables.playClick = false; player.play(); } else { playVariables.playClick = true; player.pause(); }}Copy the code

Let’s take a look at the current effect, as shown below, we have successfully created a panoramic video

Add the bottom control bar

In fact, we have already made the panoramic video above, and this operation also applies to the panorama live broadcast. Think about it, connect the video stream, turn it into a map and paste it into a geometric model, then add it to the scene, and finally render it out, with the controller, this is not exactly the same effect 😜

Now to make the above video more complete, we add bottom controls, play/pause, progress bar, full screen, window resize

// 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 > / / this paragraph style is too long I don't stick up, 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(); // add a listener function to initVideo // video can use ontimeUpdate to get the total length of the video and the current playing position // s_to_hs function is a seconds to seconds function, it can be found in the Function initVideo() {video = document.createElement("video"); 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"; }}; } // Click the progress bar and the video will go to the corresponding position. So weird) to operate the var progressContainer = document. The 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; }); / / full screen processing 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; } // listen to resize, Reset the scene size window.addEventListener("resize", function () { camera.aspect = videoContainer.clientWidth / videoContainer.clientHeight; camera.updateProjectionMatrix(); renderer.setSize( videoContainer.offsetWidth, videoContainer.offsetHeight ); }, false);Copy the code

Let’s take a look at the end result

The source address

Github can’t upload files larger than 100M (original 4K video 580M), so I compressed the video in the demo project 4 times to 62M, so the image quality may be a little bit blurred, but it doesn’t affect use.

If you are qualified, you can find the 360 panorama video by yourself or change the online video link. Here is the source code of this tutorial and the video resource download website:

  • Making the address https://github.com/ljnMeow/threeJs-FMV.git
  • 4k123 https://www.4k123.com/