»
(035)页面动画制作要点
所有的图形程序都有UI线程,也就是主线程。浏览器中的网页,只有一个线程那就是UI线程。它负责网页图形、文字的绘制以及用户交互事件的处理。比如:用户点击了一个按钮,它就会生成一个点击事件并且传递给对应的事件处理函数来运行用户指定的代码从而实现相应的事件处理功能。
当用户代码调用setTimeout()的时候,UI线程就在UI循环中加入一个时间判断标记,每次UI循环都判断一次时间间隔是否已经到达。如果到达就运行setTimeout()中指定的函数。
所以,setTimout()或者setInterval()的时间都是不准确的,要等待UI线程来判断时间间隔是否已经到达。
所以,setTimout()或者setInterval()中,每次运行用户代码的等待都会比用户传入的时间间隔参数长。
如果UI线程一直在处理需要长时间处理的循环,那么setTimeout()的函数调用就会被延后。如果有多个setTimeout(),那么依次判断依次延后。
在这里要讲的是,网页动画也是帧动画,它的画面的动的形成在于一帧一帧图像的快速绘制和显示。
在网页动画中,如果有多个短时间的setInterval(),短时间的setInterval()就很容易被长时间的事件所阻塞,比如被下载3D模型文件的事件所阻塞。
当被阻塞的setInterval()动画被唤醒时,页面的动画就会不按设定的时间间隔来显示。
所以,在网页中如果用不同时间间隔的setInterval()定时器来实现动画,尽量把页面上所有的动画放入一个setInterval()函数中,通过使用倍数于时间间隔的方式实现不同的动画。
当然,最好的方法是:setTimout()和requestAnimationFrame()函数一起使用,不论是SVG的动画还是threejs的动画。
requestAnimationFrame()函数传入的是一个动画控制函数。在调用requestAnimationFrame()之后,UI线程在完成上一UI层的函数调用之后就会立马运行该动画控制函数。
所以,我总结的使用requestAnimationFrame()控制帧率的方法是:
function animate() {
const delta = clock.getDelta();//自上一次调用getDelta()以来的时间流失。
totalDelta+=delta*1000.0;//时间流逝计入总间隔时间
if(totalDelta>=1000.0/60){//总间隔时间的计时已达到或者已超过目标帧与帧之间的间隔时间,则渲染场景
// 渲染场景
renderer.render( scene, camera );
stats.update();
++frameCount;//用于animateH5()方法,控制倍数于FPS
animateH5();//svg动画
totalDelta=clock.getDelta()*1000.0;//渲染结束。本次渲染时间的流逝计入新的帧间隔时间的计时
requestAnimationFrame(animate);//请求渲染
}else{
const delta = clock.getDelta();//自上一次调用getDelta()以来的时间流失。
totalDelta+=delta*1000.0;//时间流逝计入总间隔时间
setTimeout(()=>{
requestAnimationFrame(animate);
},1000.0/60-totalDelta);//等待时间差后再次请求渲染
}
}
在这个动画显示函数中,当完成一次动画绘制后,会立马请求渲染函数animate。
再次进入animate时,程序判断时间是否已经到达每秒60帧的渲染时间,如果已经到达或者已经超过,则直接渲染;如果还没有到达,则等待还需要等待的时间后再请求渲染。
————www.v-signon.com学习者共勉