当前位置: 永利皇宫463手机版 > Web前端 > 正文

深入理解

时间:2019-11-14 17:42来源:Web前端
深深精通 requestAnimationFrame 2017/06/26 · HTML5 ·requestAnimationFrame 初稿出处: 一像素    在Web应用中,完毕动漫效果的章程比比较多,Javascript 中得以经过反应计时器setTimeout 来完结,css3 能

深深精通 requestAnimationFrame

2017/06/26 · HTML5 · requestAnimationFrame

初稿出处: 一像素   

在Web应用中,完毕动漫效果的章程比比较多,Javascript 中得以经过反应计时器setTimeout 来完结,css3 能够接收 transition 和 animation 来贯彻,html5中的 canvas 也能够完成。除了那一个之外,html5 还提供一个专门用于供给动漫的API,那便是requestAnimationFrame,以管窥天就是恳求动漫帧。 为了深远驾驭 requestAnimationFrame 背后的法规,我们首先须求了然一下与之相关的多少个概念:

1、显示屏刷新频率

即图像在荧屏上更新的快慢,也即显示屏上的图像每分钟现身的次数,它的单位是赫兹(Hz)。 对于日常台式机Computer,那么些功效大概是60Hz, 能够在桌面上右键->显示屏分辨率->高档设置->监视器 中查看和安装。这些值的设定受显示屏分辨率、荧屏尺寸和显卡的震慑,原则上设置成让眼睛望着清爽的值都行。

市道上不足为道的显示屏有二种,即CRTLCD, CRT正是观念荧屏,LCD正是我们常说的液晶显示屏。

CRT是生龙活虎种采纳阴极射线管的显示屏,显示器上的图形图疑似由三个个因电子束击打而发光的荧光点组成,由于显像管内荧光粉受到电子束击打后发光的岁月比超级短,所以电子束必得持续攻击荧光粉使其持续发光。电子束每秒击打荧光粉的次数正是显示器刷新频率。

而对此LCD来讲,则不设有刷新频率的难题,它根本就无需刷新。因为LCD中种种像素都在任何时间任何地点不断地发光,直到不发光的电压更正并被送到调节器中,所以LCD不会有电子束击打荧光粉而滋生的闪亮现象。

因此,当您对着Computer荧屏什么也不做的情景下,荧屏也会以每秒56遍的效用正在不停的更新显示屏上的图像。为啥您认为不到这一个变化? 那是因为人的肉眼有视觉停留效应,即前意气风发副画面留在大脑的影象还未熄灭,紧接着后黄金时代副画面就跟上来了,那中间只间距了16.7ms(1000/60≈16.7), 所以会让您误认为显示器上的图疑似静止不动的。而显示器给你的这种以为是没有错,试想一下,若是刷新频率造成1次/秒,显示屏上的图像就能冒出严重的闪亮,那样就相当的轻便孳生眼睛疲劳、酸痛和头晕等症状。

2、动漫原理

根据地方的法则大家掌握,你日前所看见图像正在以每秒伍十六遍的效能刷新,由于刷新频率极高,由此你感觉不到它在刷新。而动漫片本质正是要令人及时到图像被刷新而引起变化的视觉效果,那些调换要以连贯的、平滑的议程张开连接。 这什么样技术到位这种效果与利益呢?

刷新频率为60Hz的显示屏每16.7ms刷新三次,大家在显示器每一遍刷新前,将图像的职位向左移动三个像素,即1px。那样一来,荧屏每一回刷出来的图像地点都比前二个要差1px,因而你拜望到图像在移动;由于大家人眼的视觉停留效应,当前职责的图像停留在大脑的记念还未熄灭,紧接着图像又被移到了下三个任务,因而你才会看出图像在通顺的位移,那就是视觉效果上产生的卡通。

3、setTimeout

知道了地点的概念之后,大家轻巧开掘,setTimeout 其实正是通过设置一个间隔时间来持续的更换图像的岗位,进而到达动漫效果的。但大家会发掘,利用seTimeout实现的动漫片在少数低等机上会晤世卡顿、抖动的风貌。 这种场馆包车型大巴发生有三个原因:

  • setTimeout的施行时间实际不是鲜明的。在Javascript中, setTimeout 任务被放进了异步队列中,唯有当主线程上的天职试行完之后,才会去反省该队列里的职责是不是须要最早施行,因而 setTimeout 的实在实行时间经常要比其设定的时日晚一些。
  • 刷新频率受显示屏分辨率显示器尺寸的震慑,因而分歧器械的荧屏刷新频率大概会分化,而 setTimeout只好设置三个定位的日子间距,这些日子不分明和显示屏的刷新时间一模二样。

以上两种意况都会产生setTimeout的实践步调弄收拾显示屏的根基代谢步调不相符,进而引起丢帧情状。 那为何步调不等同就能够引起丢帧呢?

第大器晚成要通晓,setTimeout的履行只是在内部存储器中对图像属性进行改变,那个变化一定要等到显示屏后一次刷新时才会被更新到显示器上。假设两岸的步子不平等,就大概会变成人中学间某风度翩翩帧的操作被抢先过去,而直接更新下生机勃勃帧的图像。倘若荧屏每间距16.7ms刷新一遍,而setTimeout每间隔10ms设置图像向左移动1px, 就能够产出如下绘制进度:

  • 第0ms: 显示器未刷新,等待中,setTimeout也未奉行,等待中;
  • 第10ms: 显示器未刷新,等待中,setTimeout开端实行并安装图像属性left=1px;
  • 第16.7ms: 显示器开头刷新,荧屏上的图像向左移动了1px, setTimeout 未进行,继续守候中;
  • 第20ms: 荧屏未刷新,等待中,setTimeout开端试行并设置left=2px;
  • 第30ms: 荧屏未刷新,等待中,setTimeout起初执行并安装left=3px;
  • 第33.4ms:显示器早先刷新,显示器上的图像向左移动了3px, setTimeout未进行,继续守候中;

从上边的绘图进程中得以观望,显示器未有立异left=2px的那风姿浪漫帧镜头,图像直接从1px的岗位跳到了3px的的任务,那就是丢帧现象,这种境况就能够唤起动漫卡顿。

4、requestAnimationFrame

与setTimeout比较,requestAnimationFrame最大的优势是由系统来支配回调函数的实施机遇。切实一点讲,即使荧屏刷新率是60Hz,那么回调函数就每16.7ms被试行一次,即使刷新率是75Hz,那么这么些日子间距就改成了1000/75=13.3ms,换句话说就是,requestAnimationFrame的步子跟着系统的底蕴代谢步伐走。它能确定保证回调函数在显示屏每趟的刷新间距中只被实施一回,那样就不会唤起丢帧现象,也不会促成动漫面世卡顿的标题。

那个API的调用很简短,如下所示:

var progress = 0; //回调函数 function render() { progress += 1; //修改图像的岗位 if (progress < 100) { //在动漫没有终止前,递归渲染 window.requestAnimationFrame(render); } } //第大器晚成帧渲染 window.requestAnimationFrame(render);

1
2
3
4
5
6
7
8
9
10
11
12
13
var progress = 0;
//回调函数
function render() {
    progress += 1; //修改图像的位置
    if (progress < 100) {
           //在动画没有结束前,递归渲染
           window.requestAnimationFrame(render);
    }
}
//第一帧渲染
window.requestAnimationFrame(render);

除去,requestAnimationFrame还会有以下多少个优势:

  • CPU节能:使用setTimeout实现的卡通片,当页面被隐形或最小化时,setTimeout 还是在后台施行动漫职责,由于这时页面处于不可以预知或不可用状态,刷新动画是一贯不意义的,完全都以荒凉CPU能源。而requestAnimationFrame则一心不一致,当页面管理未激活的状态下,该页面包车型大巴显示器刷新职责也会被系统中断,因而跟着系统步伐走的requestAnimationFrame也会告意气风发段落渲染,当页面被激活时,动画就从上次滞留的地点继续实践,有效节约了CPU开销。
  • 函数节流:在高频率事件(resize,scroll等)中,为了防备在二个刷新间距内产生频仍函数实施,使用requestAnimationFrame可确定保证各个刷新间距内,函数只被试行一遍,那样既可以保险通畅性,也能越来越好的节约函数试行的付出。一个刷新间距内函数试行数次前卫未意思的,因为显示器每16.7ms刷新壹次,数十次绘制并不会在荧屏上反映出来。

5、高雅降级

由于requestAnimationFrame目前还存在包容性难点,何况差异的浏览器还索要带差异的前缀。因而须求经过高贵降级的办法对requestAnimationFrame举行包装,优用高等天性,然后再依附不一致浏览器的情况展开回降,直止只可以利用setTimeout的情状。下边包车型大巴代码便是有人在github上提供的polyfill,详细介绍请参见github代码 requestAnimationFrame

if (!Date.now) Date.now = function() { return new Date().getTime(); }; (function() { 'use strict'; var vendors = ['webkit', 'moz']; for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) { var vp = vendors[i]; window.requestAnimationFrame = window[vp+'RequestAnimationFrame']; window.cancelAnimationFrame = (window[vp+'CancelAnimationFrame'] || window[vp+'CancelRequestAnimationFrame']); } if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) // iOS6 is buggy || !window.requestAnimationFrame || !window.cancelAnimationFrame) { var lastTime = 0; window.requestAnimationFrame = function(callback) { var now = Date.now(); var nextTime = Math.max(lastTime + 16, now); return setTimeout(function() { callback(lastTime = nextTime); }, nextTime - now); }; window.cancelAnimationFrame = clearTimeout; } }());

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
if (!Date.now)
    Date.now = function() { return new Date().getTime(); };
(function() {
    'use strict';
    
    var vendors = ['webkit', 'moz'];
    for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {
        var vp = vendors[i];
        window.requestAnimationFrame = window[vp+'RequestAnimationFrame'];
        window.cancelAnimationFrame = (window[vp+'CancelAnimationFrame']
                                   || window[vp+'CancelRequestAnimationFrame']);
    }
    if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) // iOS6 is buggy
        || !window.requestAnimationFrame || !window.cancelAnimationFrame) {
        var lastTime = 0;
        window.requestAnimationFrame = function(callback) {
            var now = Date.now();
            var nextTime = Math.max(lastTime + 16, now);
            return setTimeout(function() { callback(lastTime = nextTime); },
                              nextTime - now);
        };
        window.cancelAnimationFrame = clearTimeout;
    }
}());

1 赞 2 收藏 评论

图片 1

编辑:Web前端 本文来源:深入理解

关键词: