requestAnimationFrame
requestAnimationFrame 是浏览器专门为动画提供的 API,它的刷新频率与显示器的频率保持一致,使用该 api 可以解决用 setTimeout/setInterval 制作动画卡顿的情况。
很长时间以来,计时器和定时执行都是 JavaScript 动画最常用的工具。虽然 CSS 过渡和动画方便了Web开发者实现某些动画,但 JavaScript 动画领域多年来进展甚微。Firefox 4 率先在浏览器中为 JavaScript 动画增加了一个名为 mozRequestAnimationFrame() 方法的API。这个方法会告诉浏览器要执行动画了,于是浏览器可以 通过最优方式确定重绘的时序。自从出现之后,这个API被广泛采 用,现在作为 requestAnimationFrame() 方法已经得到各大浏 览器的支持。
1)引擎层面
setTimeout/setInterval 属于 JS引擎,requestAnimationFrame 属于 GUI引擎
JS引擎与GUI引擎是互斥的,也就是说 GUI 引擎在渲染时会阻塞 JS 引擎的计算
2)时间是否准确
requestAnimationFrame 刷新频率是固定且准确的,但 setTimeout/setInterval 是宏任务,根据事件轮询机制,其他任务会阻塞或延迟 js 任务的执行,会出现定时器不准的情况
3)性能层面
当页面被隐藏或最小化时,setTimeout/setInterval 定时器仍会在后台执行动画任务,而使用 requestAnimationFrame 当页面处于未激活的状态下,屏幕刷新任务会被系统暂停
早期定时动画
以前,在JavaScript中创建动画基本上就是使用 setInterval() 来控制动画的执行。
(function() {
function updateAnimations() {
doAnimation1();
doAnimation2();
// 其他任务
}
setInterval(updateAnimations, 100);
})();
setInterval() 和 setTimeout() 的不精确是个大问题。
requestAnimationFrame
requestAnimationFrame() 方法接收一个参数,此参数是 一个要在重绘屏幕前调用的函数。这个函数就是修改DOM样式以反映 下一次重绘有什么变化的地方。为了实现动画循环,可以把多个 requestAnimationFrame() 调用串联起来,就像以前使用 setTimeout() 时一样
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>requestAnimationFrame</title>
<style>
body {
width: 98%;
text-align: center;
}
#status {
background-color: #1890ff;
height: 20px;
border-radius: 1000px;
}
</style>
</head>
<body>
<div id="status" style="width:0;background-color: #1890ff;border-radius: 1000px;">
</div>
<button onclick="fun()">request</button>
</body>
<script>
function updateProgress() {
var div = document.getElementById("status");
console.log('div.style.width ==', div.style.width);
div.style.width = (parseInt(div.style.width) + 1) + "%";
if (parseInt(div.style.width) <= 100) {
requestAnimationFrame(updateProgress);
}
}
function fun() {
console.log('fun')
requestAnimationFrame(updateProgress);
}
</script>
</html>
cancelAnimationFrame
与 setTimeout() 类似, requestAnimationFrame() 也 返回一个请求ID,可以用于通过另一个方法 cancelAnimationFrame() 来取消重绘任务。
let requestID = window.requestAnimationFrame(()=> {
console.log('Repaint!');
});
window.cancelAnimationFrame(requestID);