使用requestAnimationFrame实现超流畅的动画效果

发布者: xiaozhimn

我们平时实现JavaScript动画效果时离不开setInterval或者setTimeout函数,这两个函数本质上相同的。
以setInterval为例,他的作用是以相同的时间间隔执行某个操作,这个时间可以自定义,利用这个特性我们就可以让然也元素跑起来。
可是这个函数有个毛病,他和显示器的刷新频率无法对应。比如说显示器每100毫秒刷新一次,setInterval函数设置的间隔执行也是100毫秒,可是我们没有办法保证显示频率刷新的时候setInterval的操作正好被执行,虽然它们的相对执行时间相同,可绝对时间不一定一致,所以setInterval制作动画的时候会出现丢帧和动画效果生硬不连贯等情况。
requestAnimFrame是H5新标准上的东西,在PC浏览器上会出现兼容问题,在移动端浏览器中可以任意使用。 次函数会接受一个回调函数,当浏览器的显示频率刷新的时候,此函数会被执行。
window.requestAnimationFrame(function( time){
    //显示频刷新的时候被执行
});
回调函数有一个参数,是一个相对的时间毫秒值,表示当前的刷新时间。
requestAnimationFrame的回调函数并不能被重复调用,这点和setInterval不同,它和setTimeout类似,回调函数只能被调用一次,只不过setTimeout可以自定义调用时间, requestAnimationFrame的调用时间则是跟着系统的刷新频率走的,所以在实现动画的时候,setTimeout比requestAnimationFrame更加灵活, requestAnimationFrame比setTimeout表现效果更加优秀。
以在3000毫秒内移动1500px距离的动画为例

setTimeout的实现方式

<div id="div" style="width:100px; height:100px; background-color:#000; position: absolute;left:0; top:0;">

</div>
let divEle = document.getElementById("div");

const distance = 1500;
const timeCount = 3000;

const intervalTime = 10;
let runCount = timeCount / intervalTime;
let moveValue = distance / runCount;

function handler() {
    let left = parseInt(divEle.style.left);
    if(left >= distance) {
        return;
    }
    divEle.style.left = left + moveValue;
    window.setTimeout(handler, intervalTime);
}

window.setTimeout(handler, intervalTime);
以上代码通过setTimeout10毫秒为间隔时间改变一次元素的位置以实现元素的动画效果, 当然, 可以通过改变这个间隔时间来微调动画效果,可是你永远没有办法确定最优方案,因为它总会和刷新频率存在交叉。

通过requestAnimationFrame我们可以给出更好的解决方案

<div id="div" style="width:100px; height:100px; background-color:#000; position: absolute;left:0; top:0;">

</div>

let divEle = document.getElementById("div");

const distance = 1500;
const timeCount = 3000;

function handler( time ) {
    if(time > timeCount) {
        time = timeCount;
    }
    divEle.style.left = time * distance / timeCount;  
    window.requestAnimationFrame( handler );
}

 window.requestAnimationFrame( handler );
如果setTimeout,handler函数也会被递归的重复调用,只是它的调用和显示的刷新频率是一致的,因此动画效果更加顺滑自然,也能找到性能和效果的最佳均衡点,得到最有的解决方案。
0赞