码农H5C3

全面了解requestAnimationFrame

2020-04-02  本文已影响0人  潜水的旱鸭子

一、前言

由于最近在做一些新尝试,瞄上了HTML5的新特性requestAnimationFrame,发现真是好用,比计时器好了不知......但话不能说太满,各有各的好处吧...,下面就来大致详细简单聊聊requestAnimationFrame到底是个啥?怎么用?有啥特色?

二、概述

requestAnimationFrame字面意思:请求动画帧。官方解释:帧动画。就是可以一帧一帧的执行动画。

那么问题来了,这个一帧的执行频率是多久?答案是:与屏幕的刷新频率同步。

也可以认为:让浏览器在显示器屏幕下次刷新时,执行一帧;那么显示器多次刷新屏幕,就执行了多帧;如果速度够快,就会形成动画。

那么显示器的刷新屏幕,也就是屏幕的刷新频率又是什么呢?

三、屏幕刷新及刷新频率

要想理解屏幕刷新频率,就要先了解显示器及显示器的原理。

目前市面上常见的显示器有两种,即CRT和LCD, CRT就是传统的大头显示器(其实现在CRT显示器已经很少见了),LCD就是我们常说的液晶显示器。

CRT显示器 LED 显示器

屏幕刷新频率是对于CRT显示器来说的,因CRT显示器是一种使用阴极射线管的显示器,屏幕上的图形图像是由一个个因电子束击打而发光的荧光点组成,由于显像管内荧光粉受到电子束击打后发光的时间很短,所以电子束必须不断击打荧光粉使其持续发光。

对于LCD来说则不存在刷新率的问题,因为LCD中每个像素都在持续不断地发光,直到不发光的电压改变并被送到控制器中,所以LCD不会有“不断充放电”而引起的闪烁现象。

但是LCD沿用的CRT显示器屏幕刷新频率的概念,即图像在屏幕上更新的速度,也即屏幕上的图像每秒钟出现的次数,它的单位是赫兹(Hz)。 并且是可以设置的(但实验证明,调高一定数值之后并没有带来什么提升)。

对于一般笔记本电脑来说,这个频率大概是60Hz,表示每秒刷新60次屏幕。windows系统可以在桌面上右键->屏幕分辨率->高级设置->监视器中查看和设置。这个值的设定受屏幕分辨率、屏幕尺寸和显卡的影响,原则上设置成让眼睛看着舒适的值都行。

四、动画的原理

因此,当你对着电脑屏幕什么也不做的情况下,显示器也会以每秒60次的频率正在不断的更新屏幕上的图像。为什么你感觉不到这个变化? 那是因为人的眼睛有视觉停留效应,即前一副画面留在大脑的印象还没消失,紧接着后一副画面就跟上来了,这中间只间隔了16.7ms(1000/60≈16.7), 所以会让你误以为屏幕上的图像是静止不动的。而屏幕给你的这种感觉是对的,试想一下,如果刷新频率变成1次/秒,屏幕上的图像就会出现严重的闪烁,这样就很容易引起眼睛疲劳、酸痛和头晕目眩等症状。

所以,如果我们能够捕捉屏幕的刷新频率,并在每次屏幕刷新时,执行某个连续的改变,那么对于人眼来说,就形成了连贯的动画。

五、requestAnimationFrame的使用

有了以上知识储备之后,我们不难明白requestAnimationFrame的执行原理。最后再补充些常识性内容:

注意没有时间参数,因为会自动随着屏幕的刷新频率自动执行,所以最终语法为:

let i = 0;
function step(timestamp) {
    console.log(i++);
    window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step);

那么如何清除呢:

let myReq;
let i = 0;
function step(timestamp) {
    console.log(i++);
    myReq = window.requestAnimationFrame(step);
}
window.requestAnimationFrame(step);

document.onclick = function(){
    window.cancelAnimationFrame(myReq);    // 专属清除方式
}

点击页面就可以停止当前执行。

但是我们不难发现,其实这么使用requestAnimationFrame实现动画,是一件非常不优雅的事。至少现在只能根据浏览器的刷新频率自动执行(一般按照60Hz算,每秒执行60次),而不能自定义每秒次数,所以我们可以简单封装一下:

function animate(cb,time){
    let myReq;    // 记录requestAnimationFrame的返回值
    let i = 1;    // 记录requestAnimationFrame的执行次数(屏幕刷新次数)
    myReq = requestAnimationFrame(function fn(){    // 开启初始requestAnimationFrame
        // 计数器 % (60/一秒钟执行的次数)
        if(i%parseInt(60/(1000/time)) == 0){
            cb();    // 执行真正要做的事情
        }
        i++;    // 记录requestAnimationFrame执行的次数
        myReq = requestAnimationFrame(fn);    // 开启下次requestAnimationFrame
        window.myReq = myReq;    // 将requestAnimationFrame返回值暴露,方便清除
    });
}

// 测试
animate(function(){
    console.log("自己封装了个计时器,好厉害呀");
}, 1000);    // 自定义执行时间
document.onclick = function(){
    // 主动控制清除动画
    cancelAnimationFrame(myReq);
}

六、requestAnimationFrame的特点

此处我们已经能够简单使用requestAnimationFrame开启动画了,当然我们还需要考虑兼容型。
不着急,在考虑兼容性之前,我们先来说说requestAnimationFrame和常规计时器相比,有什么特点:

七、requestAnimationFrame的兼容

其实作为HTML5新增的API,没有兼容性是不可能的,但只要做好处理,且在不考虑低版本浏览器的情况下,还是非常值得推荐使用的。


requestAnimationFrame 兼容性
cancelAnimationFrame 兼容性

某些略低版本的浏览器还可以通过添加前缀的方式解决兼容问题,最后我们将上一段代码的封装,考虑前缀后,略作改进:

const animate = (function(){
    const requestAnimationFrame = window.requestAnimationFrame || 
                                  window.mozRequestAnimationFrame ||
                                  window.webkitRequestAnimationFrame ||
                                  window.msRequestAnimationFrame;
    const cancelAnimationFrame =  window.cancelAnimationFrame ||
                                  window.mozCancelAnimationFrame;
    return function(cb,time){
        let myReq;
        let i = 1;
        myReq = requestAnimationFrame(function fn(){
            // 计数器 % (60/一秒钟执行的次数)
            if(i%parseInt(60/(1000/time)) == 0){
                cb();
            }
            i++;
            myReq = requestAnimationFrame(fn);
            window.myReq = myReq;
        });
    }
})();

animate(function(){
    console.log("自己封装了个计时器,好厉害呀");
}, 500);
document.onclick = function(){
    cancelAnimationFrame(myReq);
}

参考资料:
https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame


以上,如有纰漏或不同观点,欢迎留言讨论...

上一篇 下一篇

猜你喜欢

热点阅读