原生js 动画

4,385 阅读2分钟

我们经常通过setInterval或setTimeout定时修改DOM、CSS实现动画,

利用seTimeout实现的动画在某些低端机上会出现卡顿、抖动的现象。 这种现象的产生有两个原因:

  • setTimeout的执行时间并不是确定的。在Javascript中, setTimeout 任务被放进了异步队列中,只有当主线程上的任务执行完以后,才会去检查该队列里的任务是否需要开始执行,因此 setTimeout 的实际执行时间一般要比其设定的时间晚一些。
  • 刷新频率受屏幕分辨率和屏幕尺寸的影响,因此不同设备的屏幕刷新频率可能会不同,而 setTimeout只能设置一个固定的时间间隔,这个时间不一定和屏幕的刷新时间相同。

不过实现动画还可以使用requestAnimationFrame

requestAnimationFrame的方式的优势如下:

1.经过浏览器优化,动画更流畅

2.窗口没激活时,动画将停止,省计算资源

3.更省电,尤其是对移动终端

requestAnimationFrame最大的优势是

由系统来决定回调函数的执行时机。具体一点讲,如果屏幕刷新率是60Hz,那么回调函数就每16.7ms被执行一次,如果刷新率是75Hz,那么这个时间间隔就变成了1000/75=13.3ms,换句话说就是,requestAnimationFrame的步伐跟着系统的刷新步伐走。它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次,这样就不会引起丢帧现象,也不会导致动画出现卡顿的问题。

实现一个div滑动的动画,由快至慢5s结束

.sj{
    width:50px;
    height:50px;
    border:1px solid red;
    position:absolute;
    left:0
}
<div class="sj" id="sj"></div>
//ele为要移动的盒子,target为目标位置(像素),spd为计数器的频率
var ele = document.getElementById('sj')
function animate(ele,spd){
    var start = Date.now(); // remember start time
    var timer = setInterval(function() {
        var timePassed = Date.now() - start;
        var step = Math.ceil(Math.abs(timePassed - 5000)/10)
        console.log(step)
        if (timePassed >= 5000) {
            clearInterval(timer); // finish the animation after 2 seconds
            return;
        }
        ele.style.left = ele.offsetLeft + step + 'px'
    }, spd);
}

实现一个div滑动的动画,由快至慢到500px结束

function animate1(ele,target,spd){
    var timer = setInterval(function() {
        var step = (target-ele.offsetLeft)/10;
        //对步长进行二次加工(大于0向上取整,小于0向下取整)
        step = step>0?Math.ceil(step):Math.floor(step);
        //动画原理: 目标位置 = 当前位置 + 步长
        ele.style.left = ele.offsetLeft + step + "px";
        //检测缓动动画有没有停止
        if(Math.abs(target-ele.offsetLeft)<=Math.abs(step)){
            //处理小数赋值
            ele.style.left = target + "px";
            clearInterval(timer);
        }
    }, spd);
}