Three.js入门学习笔记13:动画学习

参考学习
https://www.ituring.com.cn/book/miniarticle/53322
播放设置(暂停、时间段、时间点)
https://blog.csdn.net/u014291990/article/details/103350524
放大缩小
https://blog.csdn.net/liu4071325/article/details/52302053

动画原理

动画的本质就是利用人眼的视觉暂留特性,快速变换画面,从而产生物体运动的假象。Three.js则是通过每秒中多次重绘实现动画的。

FPS是(Frames Per Second)是指每秒画面重绘的次数。FPS越大,则动画效果越平滑,FPS小于20会有画面卡滞现象。当FPS达到60,再增加帧数人眼也不会明显感觉到变化,电影的FPS标准为24,Three.js动画,一般FPS在30-60

setInterval方法

可自定义fps

setInterval(func, msec)

func是每过msec毫秒执行的函数,如果将func定义为重绘画面的函数,就能实现动画效果。setInterval函数返回一个id,如果需要停止重绘,需要使用clearInterval方法,并传入该id

需要在init函数中调用

id = setInterval(draw, 20);
//每帧中的变化(毕竟,如果每帧都是相同的,即使重绘再多次,还是不会有动画的效果),这里我们让场景中的长方体绕y轴转动
function draw() {
    mesh.rotation.y = (mesh.rotation.y + 0.01) % (Math.PI * 2);
    renderer.render(scene, camera);
}

每20毫秒就会调用一次draw函数,改变长方体的旋转值,然后进行重绘。最终得到的效果就是FPS为50的旋转长方体。
我们在HTML中添加一个按钮,按下后停止动画:

<button id="stopBtn" onclick="stop()">Stop</button>
function stop() {
    if (id !== null) {
        clearInterval(id);
        id = null;
    }
}
例子1

1.插件平移旋转
2.点击变色
3.自动旋转
4.点击停止按钮停止旋转
按钮调用draw函数时:
点击开始按钮,每点击一次,变化一次,旋转增加0.01
按钮调用start函数时:

动画核心代码

<body>
 <script src="js/three.js"></script>
 <script>
<button id="stopBtn" onclick="stop()">stop</button>
<button id="startBtn" onclick="start()">start</button>
function init() {
  // init scene
  //省略
  // init camera
  //省略
  //light
  //省略

 //加载两个由C4D用blender转成fbx再转成json的模型文件
//用mesh1 = obj1,mesh2=obj2,可以控制多个模型的动画
 var loaderC1 = new THREE.ObjectLoader();
        loaderC1.load("json/cfbx3.json", function(obj1) {
        obj1.traverse(function(child) {
            if (child instanceof THREE.Mesh) {
                child.material.side = THREE.DoubleSide;
        }
    });
        obj1.scale.multiplyScalar(3);//3倍大小 
        mesh1 = obj1; 
        obj1.position.set(0,1.3,8.5)
        obj1.rotation.z = -Math.PI;//旋转180度
        scene.add(obj1);
    }); 
    
    
      var loaderC2 = new THREE.ObjectLoader();
        loaderC2.load("json/cfbx3.json", function(obj2) {
        obj2.traverse(function(child) {
            if (child instanceof THREE.Mesh) {
                child.material.side = THREE.DoubleSide;
        }
    });
        obj2.scale.multiplyScalar(3);//3倍大小 
        mesh2 = obj2; 
        obj2.position.set(0,1.3,6)
        obj2.rotation.z = -Math.PI;//旋转180度
        scene.add(obj2);
    }); 
    //动画重绘 每20毫秒调用一次draw函数,我理解就是20毫秒重绘一次   
       id = setInterval(draw, 20);
}//init end
   //动画
  
        function draw() {
                //两个模型旋转
                mesh1.rotation.y = (mesh1.rotation.y + 0.01) % (Math.PI * 2);
                mesh2.rotation.y = (mesh1.rotation.y + 0.01) % (Math.PI * 2);
                renderer.render(scene, camera);
            }
       
        //停止按钮
         function stop() {
                if (id !== null) {
                    clearInterval(id);
                    id = null;
                }
            }  
        //重启按钮
        function start() {
                mesh1.rotation.y = (mesh1.rotation.y + 0.01) % (Math.PI * 2);
                mesh2.rotation.y = (mesh1.rotation.y + 0.01) % (Math.PI * 2);
                renderer.render(scene, camera);
                id = setInterval(draw, 20);
            }  


</script>
</body>

在这里插入图片描述

完整代码

rotationDraw和otherDraw函数每次按动按钮只执行一次,要想多次执行需要再次调用setInterval(otherDraw, 20),让它每毫秒重绘20次,因此把它放在start函数里就可以实现暂停后再播放的效果

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>3D</title>
        <meta charset="utf-8">
        <!-- 自适应 -->
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <style>
* {
    margin: 0;
    padding: 0;
}
    
</style>
    </head>
<body>
<button class="startBtn" onclick="start1()">start1</button>
<button class="startBtn" onclick="otherDraw()">start2</button>
    <script src="js/three.js"></script>
    <script type="text/javascript" src="js/OrbitControls.js"></script>
    <script>
        	var stats, light, mesh ,group;
			var camera, scene, raycaster, renderer;

			var mouse = new THREE.Vector2(), INTERSECTED;//相交的
			var radius = 100, theta = 0;
        
        init();
        
        
        function init() {
            
            
             // init scene
            scene = new THREE.Scene();
			scene.background = new THREE.Color( 0xf0f0f0 );
            
             // init camera
           camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 10000 );
           camera.position.set(15,15,15);
//           camera.lookAt(new THREE.Vector3(0,0,0));
           camera.lookAt( scene.position );
           scene.add(camera);
            

             //light
          	var light = new THREE.DirectionalLight( 0xffffff, 1 );
				light.position.set( 1, 1, 1 ).normalize();//向量属性向量转换为单位向量,方向设置为和原向量相同,长度为1
                light.intensity=1.5;//强度
				scene.add( light );

     //缓存库

    var loaderC1 = new THREE.ObjectLoader();
        loaderC1.load("json/cfbx3.json", function(obj1) {
        obj1.traverse(function(child) {
            if (child instanceof THREE.Mesh) {
                child.material.side = THREE.DoubleSide;
          
        }
    });
        obj1.scale.multiplyScalar(3);//3倍大小 
        mesh1 = obj1; 
        obj1.position.set(0,1.3,8.5)
        obj1.rotation.z = -Math.PI;//旋转180度
        scene.add(obj1);
    }); 
    
    
      var loaderC2 = new THREE.ObjectLoader();
        loaderC2.load("json/cfbx3.json", function(obj2) {
        obj2.traverse(function(child) {
            if (child instanceof THREE.Mesh) {
                child.material.side = THREE.DoubleSide;
          
        }
    });
        obj2.scale.multiplyScalar(3);//3倍大小 
        mesh2 = obj2; 
        obj2.position.set(0,1.3,6)
        obj2.rotation.z = -Math.PI;//旋转180度
        scene.add(obj2);
    }); 

            //射线
            raycaster = new THREE.Raycaster();
          /*   var raycaster = new THREE.Raycaster();
             var mouseVector = new THREE.Vector3();*/
            
            
            //renderer
				renderer = new THREE.WebGLRenderer();
				renderer.setSize( window.innerWidth, window.innerHeight );
				document.getElementsByTagName("body")[0].appendChild(renderer.domElement);
            
           //监听
           document.addEventListener( "mousedown", onDocumentMouseDown, false );
           
           //窗口变化
           window.addEventListener( "resize", onWindowResize, false );
            
            
            //插件
    var controls = new THREE.OrbitControls( camera, renderer.domElement );//camera和render的变量和照相机与渲染器设置的变量一致才行

        // 如果使用animate方法时,将此函数删除
        //controls.addEventListener( 'change', render );
        // 使动画循环使用时阻尼或自转 意思是否有惯性
        controls.enableDamping = true;
        //动态阻尼系数 就是鼠标拖拽旋转灵敏度
        //controls.dampingFactor = 0.25;
        //是否可以缩放
        controls.enableZoom = true;
        //是否自动旋转
        controls.autoRotate = true;
        //设置相机距离原点的最远距离
        controls.minDistance  = 1;
        //设置相机距离原点的最远距离
        controls.maxDistance  = 200;
        //是否开启右键拖拽
        controls.enablePan = true; 
            

       //动画重绘     
       id = setInterval(rotationDraw, 20);
            
      
        }//function结束
        
        //窗口变化
        function onWindowResize() {

				camera.aspect = window.innerWidth / window.innerHeight;
//				camera.updateProjectionMatrix();

				renderer.setSize( window.innerWidth, window.innerHeight );

			}
        
        //坐标转换
       function onDocumentMouseDown( event ) {//鼠标事件开始

				event.preventDefault();
               
				mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
				mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
           
           
                 // 通过摄像机和鼠标位置更新射线
	     raycaster.setFromCamera( mouse, camera );

	      // 计算物体和射线的焦点
	     var intersects = raycaster.intersectObjects( scene.children ,true);//射线穿过物体,自动由近到远排序


            
            //第二种
        if ( intersects.length > 0 ) {//有物体的时候
         

					if ( INTERSECTED != intersects[ 0 ].object ) {//上一次选中不等于当前的选中,就是替换的时候,intersects[ 0 ]就是当前的选中 在最前面的,它是自动排序的
                 

						if ( INTERSECTED ) 
                            if( INTERSECTED.material .length==undefined){
                            INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );//上一次选中的要换回原来的材料
                            }

						INTERSECTED = intersects[ 0 ].object;
                       if( INTERSECTED.material .length==undefined){
						INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();//把当前的材料保存起来
						INTERSECTED.material.emissive.setHex( 0xff0000 );//换颜色
                    }

					}

				} else {//选中空白处的时候
//
					if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );

					INTERSECTED = null;

				}

			}//mousedown鼠标事件结束

        //动画
  
    function rotationDraw() {
                mesh1.rotation.y = (mesh1.rotation.y + 0.01) % (Math.PI * 2);
                mesh2.rotation.y = (mesh1.rotation.y + 0.01) % (Math.PI * 2);
                renderer.render(scene, camera);
            }
        
        function otherDraw() {
                mesh1.position.y = mesh1.position.y + 1;
                mesh2.scale.x = mesh2.scale.x * 2;
                mesh2.scale.y = mesh2.scale.y * 2;
                mesh2.scale.z = mesh2.scale.z * 2;
//                mesh2.scale.multiplyScalar = mesh2.scale.multiplyScalar(2);
                renderer.render(scene, camera);
            }
       
        
         function stop() {
                if (id !== null) {
                    clearInterval(id);
                    id = null;
                }
            }  
          
        function start1() {
                id = setInterval(rotationDraw, 20);
            }  
        
         function start2() {
                id = setInterval(otherDraw, 20);
            }  
    </script>

</body>
</html>

requestAnimationFrame方法

这个方法没有写出来效果,不知为啥

init函数中

 id = requestAnimationFrame(draw);

取消动画

function stop() {
    if (id !== null) {
        cancelAnimationFrame(id);
        id = null;
    }
}

和setInterval不同的是,由于requestAnimationFrame只请求一帧画面,因此,除了在init函数中需要调用,在被其调用的函数中需要再次调用requestAnimationFrame:

function draw() {
    mesh.rotation.y = (mesh.rotation.y + 0.01) % (Math.PI * 2);
    renderer.render(scene, camera);
    id = requestAnimationFrame(draw);
}

为了支持这些浏览器,我们最好在调用之前,先判断是否定义了requestAnimationFrame以及上述函数:

var requestAnimationFrame = window.requestAnimationFrame 
        || window.mozRequestAnimationFrame
        || window.webkitRequestAnimationFrame
        || window.msRequestAnimationFrame;
window.requestAnimationFrame = requestAnimationFrame;

加载模型并控制动画播放

https://blog.csdn.net/kfyzjd2008/article/details/87000788

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值