一、帧的理解
1、帧
帧是影像或动画中的最小单位,相当于电影胶片上的每一格画面
2、帧数和帧率
帧数:指一段时间内,产生的或者播放的帧的数量帧率:指一秒内产生的或者播放的帧的数量,单位为FPS,frames per second二、requestAnimationFrame的使用
window.requestAnimationFrame()
告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。其通常执行回调函数的频率为每秒60次(约16.6ms刷新一次)。当然其刷新次数也与当下浏览器屏幕刷新次数相匹配。
1、执行刷新回调
通常,requestAnimationFrame与回调函数会构成循环嵌套,从而实现持续地帧刷新。
形如下:
function frame(){ //下一帧的渲染逻辑:例如修改canvas图形的offset等、重新计算元素的transform属性值 ..... //停止下一次的帧刷新 if(终结判断 为真) return requestAnimationFrame(frame)}
值得注意的是,当在class类中定义frame函数,并用将其作为回调函数时,一定要注意利用箭头函数解决this指向的问题 !直接requestAnimationFrame(this.frame)会使得this指向调用者window!
正确写法如下:
class Renderer{ ... frame(){ //下一帧的渲染逻辑:例如修改canvas图形的offset等、重新计算元素的transform属性值 ..... //停止下一次的帧刷新 if(终结判断 为真) return requestAnimationFrame(()=>this.frame()) }}
此时,利用箭头函数的特性【即this指向的是函数定义时,外部最近作用域中的this对象】,才可以准确调用frame函数
2、timestamp传参
该回调函数会传入 DOMHighResTimeStamp 参数,该参数与 performance.now() 的返回值相同,它表示 requestAnimationFrame()
开始执行回调函数的时刻。
*注意:若在同一帧时间内传入多个回调函数,每个回调函数中的timestamp值是相同的!
performance.now()与一些js中的time类(例如Date.now())不同的是,它不仅仅可以精确到一毫秒,而且可以以毫秒为单位作为浮点数返回,最大可精确到微秒级别
因此,当我们可以显示声明回调函数中的timestamp参数,从而准确计算这一帧中的物体状态。
例如:
const element = document.getElementById("some-element-you-want-to-animate");let start, previousTimeStamp;let done = false;function step(timestamp) { if (start === undefined) { start = timestamp; } const elapsed = timestamp - start; if (previousTimeStamp !== timestamp) { // 这里使用 Math.min() 确保元素在恰好位于 200px 时停止运动 const count = Math.min(0.1 * elapsed, 200); element.style.transform = `translateX(${count}px)`; if (count === 200) done = true; } if (elapsed < 2000) { // 2 秒之后停止动画 previousTimeStamp = timestamp; if (!done) { window.requestAnimationFrame(step); } }}window.requestAnimationFrame(step);
3、取消动画帧的渲染
requestAnimationFrame的返回值是一个long类型的非0值,可理解为请求DI,作为回调列表中的唯一标识。
我们可以利用这个ID值,传入window.cancelAnimationFrame() 以取消相应的回调函数请求。
为提高性能和延长电池寿命,当浏览器将标签页切换至后台 或者 在隐藏的<iframe>里 时,requestAnimationFrame将会暂停调用执行。
三、为什么不用setInterval进行动画帧的刷新
setInterval属于定时器,即间隔一段时间后执行代码内容。
由于JS的执行是单线程的,其遵循事件循环(event loop)机制。而setInterval定时执行回调函数属于宏任务,这种执行顺序会导致其隐藏时间误差。
如果将setInterval作为帧刷新的控制器,其在长时间执行后,由于时间误差的累积,效果会越来越不理想!从而导致动画闪烁等情况
另外,在性能方面,requestionAnimationFrame在切换标签页至后台时,会自动停止执行,而setInterval不会