requestAnimationFrame 高优先级任务

62 篇文章 2 订阅

基本使用

const id = requestAnimationFrame(function(timestamp){
	timestamp //表示 requestAnimationFrame() 开始执行回调函数的时刻。
})

window.cancelAnimationFrame(id)

为什么使用 requestAnimationFrame

比如动画的时候,一帧的时间内布局和绘制结束后还有剩余时间,JS就会拿到主线程使用权,如果 JS 某个任务执行过长,动画下一帧要开始时JS还没执行完,就会导致掉帧,出现卡顿,这时就用requestAnimationFrame(),在每一帧的时候调用,通过这个API的回调把JS任务分成更小的任务块,分到每一帧上,一帧时间到先暂停JS执行,然后下一帧绘制完成把主线程给JS。

  • 计时器一直是javascript动画的核心技术。而编写动画循环的关键是要知道延迟时间多长合适。一方面,循环间隔必须足够短,这样才能让不同的动画效果显得平滑流畅;另一方面,循环间隔还要足够长,这样才能确保浏览器有能力渲染产生的变化

  • 大多数电脑显示器的刷新频率是60Hz,大概相当于每秒钟重绘60次。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于16.6ms

  • 而setTimeout和setInterval的问题是,它们都不精确。它们的内在运行机制决定了时间间隔参数实际上只是指定了把动画代码添加到浏览器UI线程队列中以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行

    • 因为屏幕的刷新频率是 60 Hz,所以可能在 16.6ms 之内执行了多次 setTimeout task 之后才到了渲染时机并执行渲染,等到渲染时机,此时已经执行了多次setTimeout。
    • 浏览器会根据当前的浏览上下文判断是否进行渲染,它会尽量高效,只有必要的时候才进行渲染。按照规范里说的一样,因为考虑到硬件的刷新频率限制、页面性能以及页面是否存在后台等等因素,有可能执行完 setTimeout 这个 task 之后,发现还没到渲染时机,所以 setTimeout 回调了几次之后才进行渲染,此时设置的动画偏移量和上一次渲染前的差值要大于 1px 的
  • requestAnimationFrame采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销;也不会因为间隔时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果
    在这里插入图片描述

特点

  1. requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率
  2. 在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的 CPU、GPU 和内存使用量
  3. requestAnimationFrame 是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话或者在隐藏的<iframe>,动画会自动暂停,有效节省了CPU开销

使用

  • 创建定时器
function fn(timestamp){

}

下一次重绘之前更新动画帧所调用的函数(即上面所说的回调函数)。
该回调函数会被传入DOMHighResTimeStamp参数,该参数与performance.now()的返回值相同,它表示requestAnimationFrame() 开始去执行回调函数的时刻。
let timer=window.requestAnimationFrame(fn);
  • 销毁定时器
window.cancelAnimationFrame(timer) 
  • 补充performance.now
  • performance.now()方法返回一个精确到毫秒的 DOMHighResTimeStamp 。
  • 这个时间戳实际上并不是高精度的。为了降低像Spectre这样的安全威胁,各类浏览器对该类型的值做了不同程度上的四舍五入处理。(Firefox从Firefox 59开始四舍五入到2毫秒精度)一些浏览器还可能对这个值作稍微的随机化处理。这个值的精度在未来的版本中可能会再次改善;浏览器开发者还在调查这些时间测定攻击和如何更好的缓解这些攻击。
  • window.performance.now()是以一个恒定的速率慢慢增加的,它不会受到系统时间的影响(系统时钟可能会被手动调整或被NTP等软件篡改)。另外,performance.timing.navigationStart + performance.now() 约等于 Date.now()。
  • 为了提供对定时攻击和指纹的保护,performance.now()的精度可能会根据浏览器的设置而被舍弃。在Firefox中,privacy.reduceTimerPrecision偏好是默认启用的,默认值为1ms。
const t0 = window.performance.now();
doSomething();
const t1 = window.performance.now();
console.log("doSomething函数执行了" + (t1 - t0) + "毫秒.")

代码示例:
进度条:

var timer;
btn.onclick = function(){
    myDiv.style.width = '0';
    cancelAnimationFrame(timer);
    timer = requestAnimationFrame(function fn(){
        if(parseInt(myDiv.style.width) < 500){
            myDiv.style.width = parseInt(myDiv.style.width) + 5 + 'px';
            myDiv.innerHTML =     parseInt(myDiv.style.width)/5 + '%';
            timer = requestAnimationFrame(fn);
        }else{
            cancelAnimationFrame(timer);
        }    
    });
}

左右反复移动:

var e = document.getElementById("e");
    var flag = true;
    var left = 0;
    //当前执行时间
    var nowTime = 0;
    //记录每次动画执行结束的时间
    var lastTime = Date.now();
    //我们自己定义的动画时间差值
    var diffTime = 40;

    function render() {
        if(flag == true){
            if(left>=100){
                flag = false
            }
            e.style.left = ` ${left++}px`
        }else{
            if(left<=0){
                flag = true
            }
            e.style.left = ` ${left--}px`
        }
    }

    //requestAnimationFrame效果
    (function animloop() {
        //记录当前时间
        nowTime = Date.now()
        // 当前时间-上次执行时间如果大于diffTime,那么执行动画,并更新上次执行时间
        if(nowTime-lastTime > diffTime){
            lastTime = nowTime
            render();
        }
        requestAnimationFrame(animloop);

    })()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值