Skip to content

requestAnimationFrame属性

1、认识requestAnimationFrame是什么

requestAnimationFrame是用于浏览器动画的 JavaScript 方法

简而言之:requestAnimationFrame其实就是用来做动画的属性,用法上同setTimeout类似,并且可以匹配当前设备的刷新率,requestAnimationFrame() 通过自动匹配设备帧率来展示动画(多少帧就每秒执行多少次),做出可以匹配设备刷新率的动画

本质上:是一个浏览器的宏任务请求动画帧

2、作用

  • 代替定时器做更加流畅高性能的动画,也就是弥补定时器做动画不流畅的问题,
  • 高性能且可以解决setTimeout出现动画卡顿的问题
  • 弥补css无法实现的一些动画,比如:页面滚动

3、与setTimeout的相同和区分

【相同点】:

requestAnimationFrame与setTimeout都会返回一个唯一标识

【不同点】:

原理上

  • setTimeout利用递归来代替setInterval做简单的动画
  • requestAnimationFrame() 通过自动匹配设备帧率来展示动画

关闭定时器

  • 定时器上,setTimeout可以通过clearTImeout()关闭定时器。
  • requestAnimationFrame() 则使用cancelAnimationFrame()来关闭动画

参数上

  • setTimeout传入函数和时间
  • requestAnimationFrame这个方法只需要传入一个回调函数,不需要其他参数

时间间隔上

做动画都有一个时间间隔,requestAnimationFrame也有时间间隔

  • setTimeout由自定义的时间间隔来控制的
  • requestAnimationFrame与设备挂钩匹配(不受其他任务的影响)

回调函数执行次数上

  • setTimeout由自定义的时间间隔来控制的
  • requestAnimationFrame🉑️匹配设备,时间间隔上,requestAnimationFrame通常由我们传入的回调函数每秒会执行60次,但是如果你的设备的浏览器遵循W3c的标准,那么我们的函数每秒执行的次数会与你设备的刷新率相匹配。

动画运行上

  • setTimeout由自定义的时间间隔不断运行
  • requestAnimationFrame在大多浏览器中一旦页面不处于浏览器的当前标签,requestAnimationFrame就会自动停止动画。

刷新率和稳定性上

  • 使用定时器setTimeout做动画,假设显示器的刷新率为60hz【也就是每1秒播放60张动画】。为了让动画显示流畅,我们需要将定时器setTimeout的间隔时间定位 “1000/60”,【意思就是1000毫秒执行60次回调函数】,也就是1秒执行60次回调函数,大约每隔16.67毫秒会执行一次,这样就能匹配显示屏的帧率。但是依然还会有卡顿问题,因为定时器属于宏任务,而宏任务必须等待同步任务执行完成,再等微任务执行完成才会执行其中的回调函数,所以我们规定的时间间隔是不稳定并且不准确的。
  • requestAnimationFrame则匹配设备刷新率同步动画

4、使用

requestAnimationFrame的用法与setTimeout差不多,以一个元素上下移动的动画案例来对比

❤ setTimeout() 做元素上下移动动画:

plain
const box = document.querySelector('.box'); //获取元素
let timer = setTimeout(function fn() {
        let move = parseInt(getComputedStyle(box).top);//顶部距离
	if (move < 800) {
		box.style.top = move + 8 + 'px';
		setTimeout(fn, 1000/60);
	} else {
           clearTimeout(timer);
	}
}, 1000/60);

❤ requestAnimationFrame() 做元素上下移动动画:

plain
const box = document.querySelector('.box'); //获取元素
let timer = requestAnimationFrame(function fn() { 
    let move = parseInt(getComputedStyle(box).top);
	if (move < 800) {
		box.style.left = move + 8 + 'px';
		requestAnimationFrame(fn);
	} else {
            cancelAnimationFrame(timer);
	}
});

5、优缺点

优点:

  • 动画更加流畅
  • 高性能
  • 页面滚动

缺点:存在浏览器兼容问题

【兼容性问题处理-引用阮一峰大神的代码】

plain
window.requestAnimFrame = (function(){
  return  window.requestAnimationFrame       || 
          window.webkitRequestAnimationFrame || 
          window.mozRequestAnimationFrame    || 
          window.oRequestAnimationFrame      || 
          window.msRequestAnimationFrame     || 
          function( callback ){
            window.setTimeout(callback, 1000 / 60);
          };
})();

6、实例

JS
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>requestAnimationFrame 示例</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            font-family: Arial, sans-serif;
        }
        #box {
            position: absolute;
            width: 50px;
            height: 50px;
            background-color: red;
        }
        .buttons {
            position: fixed;
            top: 10px;
            left: 10px;
            z-index: 10;
        }
        button {
            padding: 10px 20px;
            margin: 5px;
            font-size: 16px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div id="box"></div>
    <div class="buttons">
        <button id="startBtn">启动动画</button>
        <button id="stopBtn">结束动画</button>
    </div>
    <script>
        const box = document.getElementById('box');
        const startBtn = document.getElementById('startBtn');
        const stopBtn = document.getElementById('stopBtn');
        let startTime = null;
        let xPos = 0;  // 初始位置
        let animationFrameId = null;  // 用于存储 requestAnimationFrame 的 ID,方便取消动画

        // 动画函数
        function animate(time) {
            if (!startTime) startTime = time;  // 初始化开始时间
            let progress = time - startTime;  // 计算经过的时间

            // 移动方块
            xPos = progress / 10;  // 根据经过的时间控制移动速度

            // 更新方块的位置
            box.style.left = xPos + 'px';

            // 如果方块没有移动到屏幕边缘,继续执行动画
            if (xPos < window.innerWidth) {
                animationFrameId = requestAnimationFrame(animate);  // 请求下一帧动画
            } else {
                // 到达屏幕边缘后自动停止动画
                cancelAnimationFrame(animationFrameId);
            }
        }

        // 启动动画
        startBtn.addEventListener('click', function() {
            // 重置位置和开始时间,启动新的动画
            startTime = null;
            xPos = 0;
            box.style.left = '0px';  // 重置方块位置
            animationFrameId = requestAnimationFrame(animate);
        });

        // 结束动画
        stopBtn.addEventListener('click', function() {
            // 如果动画还在运行,取消它
            cancelAnimationFrame(animationFrameId);
        });
    </script>
</body>
</html>

Released under the MIT License.