Three.js使用TWEEN插件实现动画(代码教程)
程序员文章站
2023-11-12 18:39:46
简介
tween.js是一个轻量级的javascript库,通过这个库可以很容易地实现某个属性在两个值之间的进行过渡,而且起始值和结束值之间的所有中间值都会自动计算出来,这个过程叫作tweening...
简介
tween.js是一个轻量级的javascript库,通过这个库可以很容易地实现某个属性在两个值之间的进行过渡,而且起始值和结束值之间的所有中间值都会自动计算出来,这个过程叫作tweening(补间)。基础请查看:tween.js补间动画插件入门
实现案例
首先需要引入tween.js插件文件
<script src="/lib/js/libs/tween.min.js"></script>
我们实例化对象,案例里面,我们创建了两个补间:tween和tweenback。tween补间定义了position的属性如何从1过渡到0,tweenback刚好相反。通过使用chain()方法可以将这两个补间衔接起来,这样当动画启动之后,程序就会在这两个补间循环。代码最后定义的是onupdate方法,这个方法通过获取当前的position的值重新生成了所有顶点的y轴的位置。
var position = {y: 1}; tween = new tween.tween(position).to({y: 0}, 5000); tween.easing(tween.easing.sinusoidal.inout); var tweenback = new tween.tween(position).to({y: 1}, 5000); tweenback.easing(tween.easing.sinusoidal.inout); tween.chain(tweenback); tweenback.chain(tween); var count = geometry.getattribute("position").count; //获取一下模型的最矮处 geometry.computeboundingbox(); var miny = geometry.boundingbox.min.y; var onupdate = function () { var y = this.y; var arr = []; for (var i = 0; i < count; i++) { arr.push(geometry.localposition[i * 3]); arr.push((geometry.localposition[i * 3 + 1]-miny) * y); arr.push(geometry.localposition[i * 3 + 2]); } geometry.getattribute("position").array = new float32array(arr); geometry.getattribute("position").needsupdate = true; }; tween.onupdate(onupdate); tweenback.onupdate(onupdate);
补间动画需要在模型加载完成后就启动,所以我们在下面的函数末尾调用tween.start()。
tween.start();
当补间启动后,我们还需要告知tween.js库什么时候来更新所有的补间,所以我们在每次刷新时,调用tween.update()方法:
function render() { tween.update(); renderer.render(scene, camera); }
案例代码
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>title</title> <style type="text/css"> html, body { margin: 0; height: 100%; } canvas { display: block; } </style> </head> <body onload="draw();"> </body> <script src="/lib/three.js"></script> <script src="/lib/js/loaders/plyloader.js"></script> <script src="/lib/js/controls/orbitcontrols.js"></script> <script src="/lib/js/libs/stats.min.js"></script> <script src="/lib/js/libs/tween.min.js"></script> <script src="/lib/js/libs/dat.gui.min.js"></script> <script> var renderer; function initrender() { renderer = new three.webglrenderer({antialias: true}); renderer.setsize(window.innerwidth, window.innerheight); //告诉渲染器需要阴影效果 renderer.setclearcolor(0xffffff); document.body.appendchild(renderer.domelement); } var camera; function initcamera() { camera = new three.perspectivecamera(45, window.innerwidth / window.innerheight, 0.1, 1000); camera.position.set(0, 0, 50); camera.lookat(new three.vector3(0, 0, 0)); } var scene; function initscene() { scene = new three.scene(); } //初始化dat.gui简化试验流程 var gui; function initgui() { //声明一个保存需求修改的相关数据的对象 gui = {}; var datgui = new dat.gui(); //将设置属性添加到gui当中,gui.add(对象,属性,最小值,最大值) } var light; function initlight() { scene.add(new three.ambientlight(0x444444)); light = new three.pointlight(0xffffff); light.position.set(0, 50, 50); //告诉平行光需要开启阴影投射 light.castshadow = true; scene.add(light); } function initmodel() { //辅助工具 var helper = new three.axeshelper(50); scene.add(helper); var loader = new three.plyloader(); loader.load("/lib/models/ply/binary/lucy100k.ply", function (geometry) { //更新顶点的法向量 geometry.computevertexnormals(); //创建纹理,并将模型添加到场景道中 var material = new three.meshstandardmaterial({color: 0x0055ff}); var mesh = new three.mesh(geometry, material); mesh.rotation.y = math.pi; mesh.scale.set(0.02, 0.02, 0.02); scene.add(mesh); //保存一份默认的顶点信息 var position = geometry.getattribute("position").array; geometry.localposition = []; for (var i = 0; i < position.length; i++) { geometry.localposition.push(position[i]); } //初始化tween动画 inittween(geometry); }); } //添加tween动画 var tween; function inittween(geometry) { var position = {y: 1}; tween = new tween.tween(position).to({y: 0}, 5000); tween.easing(tween.easing.sinusoidal.inout); var tweenback = new tween.tween(position).to({y: 1}, 5000); tweenback.easing(tween.easing.sinusoidal.inout); tween.chain(tweenback); tweenback.chain(tween); var count = geometry.getattribute("position").count; //获取一下模型的最矮处 geometry.computeboundingbox(); var miny = geometry.boundingbox.min.y; var onupdate = function () { var y = this.y; var arr = []; for (var i = 0; i < count; i++) { arr.push(geometry.localposition[i * 3]); arr.push((geometry.localposition[i * 3 + 1]-miny) * y); arr.push(geometry.localposition[i * 3 + 2]); } geometry.getattribute("position").array = new float32array(arr); geometry.getattribute("position").needsupdate = true; }; tween.onupdate(onupdate); tweenback.onupdate(onupdate); tween.start(); } //初始化性能插件 var stats; function initstats() { stats = new stats(); document.body.appendchild(stats.dom); } //用户交互插件 鼠标左键按住旋转,右键按住平移,滚轮缩放 var controls; function initcontrols() { controls = new three.orbitcontrols(camera, renderer.domelement); // 如果使用animate方法时,将此函数删除 //controls.addeventlistener( 'change', render ); // 使动画循环使用时阻尼或自转 意思是否有惯性 controls.enabledamping = true; //动态阻尼系数 就是鼠标拖拽旋转灵敏度 //controls.dampingfactor = 0.25; //是否可以缩放 controls.enablezoom = true; //是否自动旋转 controls.autorotate = false; controls.autorotatespeed = 0.5; //设置相机距离原点的最远距离 controls.mindistance = 1; //设置相机距离原点的最远距离 controls.maxdistance = 200; //是否开启右键拖拽 controls.enablepan = true; } function render() { tween.update(); renderer.render(scene, camera); } //窗口变动触发的函数 function onwindowresize() { camera.aspect = window.innerwidth / window.innerheight; camera.updateprojectionmatrix(); render(); renderer.setsize(window.innerwidth, window.innerheight); } function animate() { //更新控制器 render(); //更新性能插件 stats.update(); controls.update(); requestanimationframe(animate); } function draw() { initgui(); initrender(); initscene(); initcamera(); initlight(); initmodel(); initcontrols(); initstats(); animate(); window.onresize = onwindowresize; } </script> </html>