window.requestAnimationFrame
介绍 RAF
requestAnimationFrame 是专门为实现高性能的帧动画而设计的一个API
-
想要动画流畅,更新频率要 60帧/s ,即 16.67ms 更新一次视图
-
setTimeout 要手动控制频率, 而 RAF 浏览器会自动控制
-
后台标签或隐藏 iframe 中,RAF 会暂停,而 setTimeout 依然执行
setTimeout和RAF示例一
3s 把宽度 100px 变成 640px,即增加 540px
60 帧/s, 那么3s 可变化 180 帧,每次变化 3px
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<style>
#div1{
width: 100px;
height: 100px;
background: red;
position: absolute;
left: 0;
top: 0;
zoom: 1;
}
</style>
<body>
<div id="div1">
<p>hello</p>
</div>
<script>
window.onload = function() {
const div1 = document.getElementById('div1')
let curWidth = 100
const maxWidth = 640
// setTimeout
// function animate() {
// curWidth = curWidth + 3
// div1.style.width = curWidth + 'px'
// if (curWidth < maxWidth) {
// // 自己控制时间
// setTimeout(animate, 16.7)
// }
// }
// animate()
// RAF
function animate2() {
curWidth = curWidth + 3
div1.style.width = curWidth + 'px'
if (curWidth < maxWidth) {
// 时间不用自己控制
requestAnimationFrame(animate2)
}
}
requestAnimationFrame(animate2)
}
</script>
</body>
</html>
setInterval和RAF示例一
将一个元素不停地做左右运动
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<style>
#div1{
width: 100px;
height: 100px;
background: red;
position: absolute;
left: 0;
top: 0;
zoom: 1;
}
</style>
<body>
<div id="div1">
<p>hello</p>
</div>
<script>
var e = document.getElementById("div1");
var flag = true;
var left = 0;
function render() {
if (flag == true) {
if (left >= 100) {
flag = false
}
e.style.left = ` ${left++}px`
} else {
if (left <= 0) {
flag = true
}
e.style.left = ` ${left--}px`
}
}
// setInterval效果
// setInterval(function(){
// render()
// }, 1000/60)
// requestAnimationFrame 效果
(function animloop() {
render();
window.requestAnimationFrame(animloop);
})();
</script>
</body>
</html>
window.cancelAnimationFrame()
怎么停止 requestAnimationFrame ?
cancelAnimationFrame() 接收一个参数 requestAnimationFrame 默认返回一个id,cancelAnimationFrame 只需要传入这个id就可以停止了。
var e = document.getElementById("div1");
var flag = true;
var left = 0;
var rafId = null
function render() {
if (flag == true) {
if (left >= 100) {
flag = false
}
e.style.left = `${left++}px`
}else{
if(left <= 0){
flag = true
}
e.style.left = `${left--}px`
}
}
// requestAnimationFrame效果
(function animloop(time) {
render();
rafId = requestAnimationFrame(animloop);
// 如果left等于50 停止动画
if (left == 50){
cancelAnimationFrame(rafId)
}
})();
如何将requestAnimationFrame帧率降低?
默认16.7ms 更新一次视图,我们修改为每40ms更新一次
var e = document.getElementById("div1");
var flag = true;
var left = 0;
// 当前执行时间
var nowTime = 0;
// 记录每次动画执行结束的时间
var lastTime = Date.now();
// 我们自己定义的动画时间差值
var diffTime = 40;
function render() {
if (flag == true) {
if (left >= 100) {
flag = false
}
e.style.left = `${left++}px`
} else {
if (left <= 0) {
flag = true
}
e.style.left = `${left--}px`
}
}
// requestAnimationFrame效果
(function animloop() {
// 记录当前时间
nowTime = Date.now()
// 当前时间-上次执行时间如果大于 diffTime,那么执行动画,并更新上次执行时间
if (nowTime - lastTime > diffTime) {
lastTime = nowTime
render();
}
requestAnimationFrame(animloop);
})()
RAF Advantage
requestAnimationFrame 比起 setTimeout、setInterval的优势:
1、requestAnimationFrame 由系统来决定回调函数的执行时机。具体一点讲,如果屏幕刷新率是 60Hz,那么回调函数就每 16.7ms 被执行一次,如果刷新率是75Hz,那么这个时间间隔就变成了 1000/75=13.3ms,换句话说就是,requestAnimationFrame 的步伐跟着系统的刷新步伐走。它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次,这样就不会引起丢帧现象,也不会导致动画出现卡顿的问题。
2、CPU节能:使用setTimeout实现的动画,当页面被隐藏或最小化时,setTimeout 仍然在后台执行动画任务,由于此时页面处于不可见或不可用状态,刷新动画是没有意义的,完全是浪费CPU资源。而requestAnimationFrame则完全不同,当页面处于未激活的状态下,该页面的屏幕刷新任务也会被系统暂停,因此跟着系统步伐走的requestAnimationFrame也会停止渲染,当页面被激活时,动画就从上次停留的地方继续执行,有效节省了CPU开销。
3、函数节流:在高频率事件(resize,scroll等)中,为了防止在一个刷新间隔内发生多次函数执行,使用requestAnimationFrame可保证每个刷新间隔内,函数只被执行一次,这样既能保证流畅性,也能更好的节省函数执行的开销。一个刷新间隔内函数执行多次时没有意义的,因为显示器每16.7ms刷新一次,多次绘制并不会在屏幕上体现出来。