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);
