前言
当前市场上WebGL开发的前端页面大多都会涉及到动画,如商品展示页面的商品放大和旋转类的效果。如果没有动画的需求,一定程度上直接让UI出图或者使用canvas(2d)实现即可,还能减少浏览器的开销。所以在三维的世界中,制作动画是一个至关重要的部分。本章在上一章的代码上,制作一个旋转的立方体demo。
周期性渲染
动画的本质是在快速不断的切换图片,在Three.js中,每执行一次渲染器对象WebGLRenderer的render()函数,浏览器就会渲染出一帧图像并显示在canvas上,这也就是说,你想实现动画就需要按照一定的周期不停的调用render方法,从而不停更新canvas上的图像实现动画的效果。
对于我来说,如何周期性调用一个函数的方法,我最先想到的就是setInterval()。 setInterval()是一个周期性函数,就像一个定时器,每隔多少毫秒ms执行一次某个函数。
// 间隔25ms周期性调用函数fun
setInterval(function(){...},25)
为实现立方体的旋转,需要不停的变更立方体的角度。mesh.rotateY(0.01)使立方体网格模型绕y轴旋转0.01弧度。
mesh.rotateY(0.01);//每次绕y轴旋转0.01弧度
合并上面的代码,如下:
function render(){
renderer.render(scene, camera);
mesh.rotateY(0.01) //单位弧度
}
//setInterval(render,25)
setInterval(function(){
render()
},25)
效果如下:
渲染频率
动画想要流畅,就需要尽可能在一秒中绘制更多的图片,但绘制太多需要消耗大量的性能,通常来说因为人眼的残影现象,所以只要两帧画面切换时间高于1/24秒,所以我们通常将渲染频率控制在每秒30~60次,人的视觉效果都很正常,也可以兼顾渲染性能。
requestAnimationFrame
前面讲解threejs动画效果,使用了setInterval()函数,实际开发中,为了更好的利用浏览器渲染,可以使用函数requestAnimationFrame()代替setInterval()函数,requestAnimationFrame()和setInterval()一样都是浏览器window对象的方法。 setInterval 与 requestAnimationFrame 的区别:
引擎层面:
setInterval 属于 JS 引擎,存在事件轮询,存在事件队列。
requestAnimationFrame 属于 GUI 引擎,发生在渲 染过程的中重绘重排部分,与电脑分辨路保持一致。
性能层面:
当页面被隐藏或最小化时,定时器 setTimeout 仍在后台执行动画任 务。
当页面处于未激活的状态下,该页面的屏幕刷新任 务会被系统暂停,requestAnimationFrame 也会停止。
应用层面:
利用 setInterval,这种定时机制去做动画,模拟固定时间刷新页面。
requestAnimationFrame 由浏览器专门为动画提供 的 API,在运行时浏览器会自动优化方法的调用,在特定性环境下可以有效节省了 CPU 开销。
对上面的代码进行改造:
function render() {
renderer.render(scene,camera);//执行渲染操作
mesh.rotateY(0.01);//每次绕y轴旋转0.01弧度
requestAnimationFrame(render);//请求再次执行渲染函数render
}
render();
均匀旋转
实际上setInterval和requestAnimationFrame每次执行的间隔可能都不一致,可以通过计算间隔时间差来实现,物体的匀速运动,代码如下。但大多情况下肉眼是分辨不出来的,除非你页面存在很长的同步运算堵塞住页面了,才会明显察觉到异常。
let T0 = new Date();//上次时间
function render() {
let T1 = new Date();//本次时间
let t = T1-T0;//时间差
T0 = T1;//把本次时间赋值给上次时间
requestAnimationFrame(render);
renderer.render(scene,camera);//执行渲染操作
mesh.rotateY(0.001*t);//旋转角速度0.001弧度每毫秒
}
render();