Appearance
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>