Threejs makes super cool 3D photo album

foreword

Some time ago, the author learned threejs and learned some basic usage methods, which aroused my interest in making a related 3D demo, so I thought of making a 3D photo frame album, and at the same time referred to some other author's methods on the Internet to implement it manually. For a moment, the following are the relevant steps of this demo. For convenience, it is mainly implemented in HTML and imports the corresponding JS module file.

The demo effect is as follows:

3d (2).gif

main idea

  1. First, initialize the size of the photo, the global style, etc., and create an empty container with an id of container, which is used to hold the 3D elements and click on any photo to enlarge the container of the photo, and then perform the main 3D operate.
  2. Find the threejs and tweenjs packages from the npm official website , download them to this project, and then import the corresponding files.
  3. Create 3D basic three elements - scene ( Scene ), camera ( Camera ), renderer ( Renderer ). The renderer uses CSS3DRenderer , mainly to apply hierarchical 3D transformations to DOM elements through the transform attribute of CSS3; then introduce the orbit controller OrbitControls , which can make the camera orbit around the target; initialize the three-dimensional coordinates Vector3 and the ball The coordinate Spherical is mainly used to get the location of the photo.
  4. Dynamically add the required dom structure for placing photos - set the photo path, size and class name (to facilitate adding picture styles). Since this structure is an HTML element, it cannot be directly added to the scene, it needs to be converted into a 3D object (with the help of CSS3DObject ), and finally each photo element is added to the scene.
  5. After setting the size and style of the pictures, it is time to start placing them in their corresponding positions. Apply mathematical formulas to set the position of each picture. Since these pictures are initially at the same position, in order to make these pictures animate to the specified position, TWEEN tween animation is used here—the tween engine can calculate from the start animation point to the end animation Values ​​between points to produce a smooth animation effect. This will form a spherical photo wall!
  6. Then write the animate function to implement TWEEN, controls, and renderer update rendering, and make recursive repeated calls .
  7. Finally, if you want to achieve the effect of zooming in on any photo by clicking on it, you mainly rely on monitoring the click event of the mouse. As long as you click on the photo, a well-written function will be triggered (this function mainly realizes the position of the enlarged photo. ,size).

main implementation

HTML structure

 <div id="container"></div> //放球体照片墙的结构
 <div id='popup'></div> //主要用来放照片放大后的结构

css style

 <style>
        html, body {
            height: 100%;
        }
        body {
            background-color: #000000;
            margin: 0;
            overflow: hidden;
        }
        .element { //照片大小
            width: 120px;
            height: 160px;
        }

            .element:hover { //鼠标悬浮在照片上的样式
            box-shadow: 0px 0px 12px rgba(0,255,255,0.75);
            border: 1px solid rgba(127,255,255,0.75);
        }
    </style>

JS implementation

1. Prepare

First import the relevant animation package. Use npm i threethe command to download the threejs package, and use npm i @tweenjs/tween.jsthe command to download the tweenjs package. You can go to the npm official website to check the usage by yourself.

Due to the introduction of threejs and tweenjs animations in HTML, additional JS templates are used.

 <script type="importmap">
{
	"imports": {
            "three": "./three/build/three.module.js",
            "three/addons/": "./three/examples/jsm/",
            "@tweenjs/tween.js": "/node_modules/@tweenjs/tween.js/dist/tween.esm.js"
	}
}
</script>

The following codes are implemented in the following code blocks to achieve animation effects.

<script type="module">
//实现动画
</script>

2. Import files and create basic elements

import * as THREE from "three";
import * as TWEEN from '@tweenjs/tween.js'
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { CSS3DRenderer } from 'three/addons/renderers/CSS3DRenderer.js';
import { CSS3DObject } from 'three/addons/renderers/CSS3DRenderer.js';

 // 场景
const scene = new THREE.Scene();

 // 镜头
const camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 3400;	
    
// 渲染器
const renderer = new CSS3DRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.getElementById( 'container' ).appendChild( renderer.domElement );

//坐标初始化
const vector = new THREE.Vector3(); //三维坐标
const spherical = new THREE.Spherical();//球坐标

//轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.autoRotate = true; //为true时,相机自动围绕目标旋转,但必须在animation循环中调用update()

var objects = []; //存放转化为3D的照片对象
var spheres=[] //用来存放目标对象的位置


init(); //初始化并形成球体照片墙
animate();//每隔一段时间渲染

3. Compilation and implementation of main related functions

Implement the init() function . This function realizes the size, style and placement of photos, and stores the photos to be displayed in advance in the project directory.

Since the added pictures are all HTML elements, if you want to add them to the scene, you need to convert them into 3D objects, so you use the CSS3DObject constructor.

function init() {
//放图片
 for (i=1; i < 125; i++ ) { //125为照片的个数,随便放多少,不过要适中
    var element = document.createElement( 'div' );
    element.className = 'element'; //给图片加类名即设置对应的图片大小		
    element.style.backgroundImage = 'url(./photo/' + i + '.jpg)'; // 背景图片 图片名称是 1...118.jpg
    element.style.backgroundSize = 'cover'; //保持图像的宽高比例,将图片缩放到正好完全覆盖定义的背景区域
    element.name = i ; // 给元素的name属性赋值,以便获取鼠标点击的当前值
    var object = new CSS3DObject( element );//可以将HTML元素作为纹理添加到3D对象中,从而创建有趣的3D特效	
    scene.add( object );
    objects.push( object );//为了知道被添加到照片元素的个数
 }
 var l = objects.length
 
 // 根据球形排列公式计算每个元素的位置
 for( var i = 0; i <= l; i ++ ) {
     //该部分为固定的数学公式
    var phi = Math.acos( -1 + ( 2 * i ) / l );
    var theta = Math.sqrt( l * Math.PI ) * phi;
    // 计算元素在球面上的坐标
    var x = 800 * Math.cos(theta) * Math.sin(phi);//800代表球的半径
    var y = 800 * Math.sin(theta) * Math.sin(phi);
    var z = 800 * Math.cos(phi);
    
    var object = new THREE.Object3D();
    object.position.set(x,y,z)//设置对象的位置
    
    vector.copy( object.position ).multiplyScalar( 2 );//将该向量与所传入的标量2进行相乘。
    object.lookAt( vector );//vector这个变量的作用,它用来作为'目标位置',使用这个方法让这个位置的对象object看向vector这一点所在的方向
    spheres.push( object );
 }
  transform( spheres, 2000 );//动画转换
}

After setting object.position.set(x,y,z)the position, a ball can almost be formed, but at this time each photo is erected, and the viewing point of the object needs to be set, so that the camera has a curvature when taking pictures.

To be honest, I am not very clear about the mathematical formula of this part, which is written and pasted from other authors.

Implement the transform function. This function mainly uses tweenjs tween animation, mainly to generate animation by transitioning these data through tween.

 function transform( spheres, duration ) {
   for ( var i = 0; i < objects.length; i ++ ) {
     var object = objects[ i ];
     var target = spheres[ i ];
      new TWEEN.Tween( object.position ) //过渡图片移动的位置
         .to( { x: target.position.x, y: target.position.y, z: target.position.z }, Math.random() * duration + duration )//改变当前模型的位置 
         .easing( TWEEN.Easing.Exponential.InOut )//运动曲线
         .start();//开启动画
            
      new TWEEN.Tween( object.rotation )//过渡图片旋转
          .to( { x: target.rotation.x, y: target.rotation.y, z: target.rotation.z }, Math.random() * duration + duration )
          .easing( TWEEN.Easing.Exponential.InOut )
          .start();
        }

    }

Tween function method: start ——Start tween animation; to ——Control the motion form and direction of tween; Easing ——Easing function. The detailed method can be checked on TWEENJS official website or other ways.

Implement the animate function, and call this function at regular intervals through requestAnimationFrame.

 function animate() {				
    TWEEN.update();					
    controls.update();				
    renderer.render(scene, camera);	
    requestAnimationFrame( animate );
 }

At this point, the sphere photo wall is basically completed! Next, if you click on a picture to zoom in, you need to bind the mouse down event to the window.

// 监听鼠标单击事件
window.addEventListener('mousedown', clickMouse);

 // 鼠标单击事件
    function clickMouse(e) {			
        if(!e){
            let e = window.event;
        }								
        let tname = e.target.name; //获取点击图片的名称				
        if (typeof(tname) == "undefined" || tname =='') {//鼠标点击的不是照片		
            let div = document.getElementById("popup");
            div.style.display = 'none'; //隐藏元素			
        }else{				
            Popup(tname);
        }
       
    
        // 放大的图片	
        function Popup(tname) {
            let w = window.innerWidth;
            let h = window.innerHeight;
            let div = document.getElementById("popup"); 					
            div.style.display = 'block'; //显示元素			
            div.style.backgroundImage = 'url(./photo/' + tname + '.jpg)';
            div.style.backgroundSize = 'cover';
            div.style.height = h*0.7 + 'px';
            div.style.width = h*0.6 +'px';					
            div.style.position = 'absolute';
            div.style.left = '0px';
            div.style.right = '0px';
            div.style.top = '0px';
            div.style.bottom = '0px';
            div.style.margin = 'auto';	//居中位置
            div.style.borderRadius = '5px'; // 圆角
        }
    }

At this point, the 3D sphere photo wall is complete~ For relevant knowledge and details, you can check it on the Threejs official website !

conclusion

This is the end of this article. Due to my limited experience, there will inevitably be mistakes. Welcome to correct me . If you think this article is helpful to you, please like and collect it❤❤❤, your like is the motivation to continue writing, thank you for your support. If you think there is a better way, please comment and make suggestions!

Guess you like

Origin juejin.im/post/7254039378195677241