深入理解定时器系列第二篇——被誉为神器的requestAnimationFramewww.129028.com金沙:

日期:2020-05-07编辑作者:Web前端

时间: 2019-09-09阅读: 127标签: 动画

前面的话

  与setTimeoutsetInterval不同,requestAnimationFrame不需要设置时间间隔。这有什么好处呢?为什么requestAnimationFrame被称为神器呢?本文将详细介绍HTML5新增的定时器requestAnimationFrame

 

为什么要说它,源于看到的一道面试题:问题是用js实现一个无限循环的动画。

引入

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

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

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

  requestAnimationFrame采用系统时间间隔,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,增加开销;也不会因为间隔时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果

 

首先想到的是定时器

特点

  【1】requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率

  【2】在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量

  【3】requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销

 

!doctype htmlhtml lang="en"head titleDocument/title style #e{ width: 100px; height: 100px; background: red; position: absolute; left: 0; top: 0; zoom: 1; } /style/headbodydiv /divscript var e = document.getElementById("e"); var flag = true; var left = 0; 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` } } setInterval(function(){ render() },1000/60)/script/body/html

使用

  requestAnimationFrame的用法与settimeout很相似,只是不需要设置时间间隔而已。requestAnimationFrame使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。它返回一个整数,表示定时器的编号,这个值可以传递给cancelAnimationFrame用于取消这个函数的执行

requestID = requestAnimationFrame(callback); 

//控制台输出1和0
var timer = requestAnimationFrame(function(){
    console.log(0);
}); 
console.log(timer);//1

  cancelAnimationFrame方法用于取消定时器

//控制台什么都不输出
var timer = requestAnimationFrame(function(){
    console.log(0);
}); 
cancelAnimationFrame(timer);

  也可以直接使用返回值进行取消

var timer = requestAnimationFrame(function(){
    console.log(0);
}); 
cancelAnimationFrame(1);

 

可以说是完美实现!

兼容

  IE9-浏览器不支持该方法,可以使用setTimeout来兼容

【简单兼容】

if (!window.requestAnimationFrame) {
    requestAnimationFrame = function(fn) {
        setTimeout(fn, 17);
    };    
}

【严格兼容】

if(!window.requestAnimationFrame){
    var lastTime = 0;
    window.requestAnimationFrame = function(callback){
        var currTime = new Date().getTime();
        var timeToCall = Math.max(0,16.7-(currTime - lastTime));
        var id  = window.setTimeout(function(){
            callback(currTime + timeToCall);
        },timeToCall);
        lastTime = currTime + timeToCall;
        return id;
    }
}

if (!window.cancelAnimationFrame) {
    window.cancelAnimationFrame = function(id) {
        clearTimeout(id);
    };
}

 

至于时间间隔为什么是1000/60,这是因为大多数屏幕渲染的时间间隔是每秒60帧。

应用

  现在分别使用setInterval、setTimeout和requestAnimationFrame这三个方法制作一个简单的进制度效果

【1】setInterval

<div id="myDiv" style="background-color: lightblue;width: 0;height: 20px;line-height: 20px;">0%</div>
<button id="btn">run</button>
<script>
var timer;
btn.onclick = function(){
    clearInterval(timer);
    myDiv.style.width = '0';
    timer = setInterval(function(){
        if(parseInt(myDiv.style.width) < 500){
            myDiv.style.width = parseInt(myDiv.style.width) + 5 + 'px';
            myDiv.innerHTML =     parseInt(myDiv.style.width)/5 + '%';    
        }else{
            clearInterval(timer);
        }        
    },16);
}
</script>

【2】setTimeout

<div id="myDiv" style="background-color: lightblue;width: 0;height: 20px;line-height: 20px;">0%</div>
<button id="btn">run</button>
<script>
var timer;
btn.onclick = function(){
    clearTimeout(timer);
    myDiv.style.width = '0';
    timer = setTimeout(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 = setTimeout(fn,16);
        }else{
            clearTimeout(timer);
        }    
    },16);
}
</script>

【3】requestAnimationFrame

<div id="myDiv" style="background-color: lightblue;width: 0;height: 20px;line-height: 20px;">0%</div>
<button id="btn">run</button>
<script>
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);
        }    
    });
}
</script>

既然setInterval可以搞定为啥还要用requestAnimationFrame呢?最直观的感觉就是,添加api的人是个大神级牛人,我只能怀疑自己。

所以搜索相关问题发现以下两点

requestAnimationFrame 比起 setTimeout、setInterval的优势主要有两点:

1、requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。2、在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的的cpu,gpu和内存使用量。

直接上代码:

!doctype htmlhtml lang="en"head titleDocument/title style #e{ width: 100px; height: 100px; background: red; position: absolute; left: 0; top: 0; zoom: 1; } /style/headbodydiv /divscript var e = document.getElementById("e"); var flag = true; var left = 0; 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() { render(); window.requestAnimationFrame(animloop); })();/script/body/html

我没有添加各个浏览器的兼容写法,这里只说用法。

效果是实现了,不过我想到两个问题。

本文由www.129028.com金沙发布于Web前端,转载请注明出处:深入理解定时器系列第二篇——被誉为神器的requestAnimationFramewww.129028.com金沙:

关键词:

[译]优秀的前端开发者为什么难找(Why is it so hard to find a go

时间: 2019-09-10阅读: 165标签: 开发者 原作者: Ilias Ismanalijev 这是一个合理的问题。对外行而言,前端开发一定看起来...

详细>>

node.js含有%百分号时,发送get请求时浏览器地址自动编码的问题

时间: 2019-09-09阅读: 193标签:请求目前浏览器会对地址,进行编码,比如这个文件名: sales报表.png 在发到后台时,会自...

详细>>

d3.js制作连线动画图和编辑器www.129028.com金沙

时间: 2019-09-06阅读: 109标签:d3本项目使用主要d3.jsv4制作,分两部分,一个是实际展示的连线动画图,另一个是管理人...

详细>>

深入学习jQuery样式操作【www.129028.com金沙】,深入学习jquery样式

时间: 2019-09-07阅读: 108标签: 函数 内容目录 深入学习jQuery样式操作,深入学习jquery样式 × toggleClass()函数用于切换当前...

详细>>