# requestAnimationFrame 动画帧
rAF是requestAnimationFrame的简称;
window.requestAnimationFrame() 告诉浏览器——希望执行一个动画,并且要求在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
注意:若想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用window.requestAnimationFrame()
window.requestAnimationFrame(callback);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style media="screen">
body {background:black; text-align: center;}
#c1 {background:#FFF}
</style>
<script>
window.onload=function (){
let oC=document.getElementById('c1');
let gd=oC.getContext('2d');
let left=100;
requestAnimationFrame(next);
function next(){
gd.clearRect(0,0,oC.width,oC.height);
left+=0.2;
gd.strokeRect(left, 100, 200, 150);
//
requestAnimationFrame(next);
}
};
</script>
</head>
<body>
<canvas id="c1" width="800" height="600"></canvas>
</body>
</html>
# setTimeout
如果前面的代码影响了性能,就会导致 setTimeout 不会按期执行。可以通过代码去修正 setTimeout,从而使定时器相对准确
let period = 60 * 1000 * 60 * 2
let startTime = new Date().getTime()
let count = 0
let end = new Date().getTime() + period
let interval = 1000
let currentInterval = interval
function loop() {
count++
// 代码执行所消耗的时间
let offset = new Date().getTime() - (startTime + count * interval);
let diff = end - new Date().getTime()
let h = Math.floor(diff / (60 * 1000 * 60))
let hdiff = diff % (60 * 1000 * 60)
let m = Math.floor(hdiff / (60 * 1000))
let mdiff = hdiff % (60 * 1000)
let s = mdiff / (1000)
let sCeil = Math.ceil(s)
let sFloor = Math.floor(s)
// 得到下一次循环所消耗的时间
currentInterval = interval - offset
console.log('时:'+h, '分:'+m, '毫秒:'+s, '秒向上取整:'+sCeil, '代码执行时间:'+offset, '下次循环间隔'+currentInterval)
setTimeout(loop, currentInterval)
}
setTimeout(loop, currentInterval)
# window.requestAnimationFrame()
取消一个先前通过调用window.requestAnimationFrame()方法添加到计划中的动画帧请求.
用动画帧优化替代setInterval
function setInterval(callback, interval) {
let timer
const now = Date.now
let startTime = now()
let endTime = startTime
const loop = () => {
timer = window.requestAnimationFrame(loop)
endTime = now()
if (endTime - startTime >= interval) {
startTime = endTime = now()
callback(timer)
}
}
timer = window.requestAnimationFrame(loop)
return timer
}
let a = 0
setInterval(timer => {
console.log(1)
a++
if (a === 3) cancelAnimationFrame(timer)
}, 1000)
- requestAnimationFrame调用的函数还有一个参数,可以自动记录时间,根据需求可以进行一些判断和拦截。
<div id="div" style="width:100px; height:100px; background-color:#000; position: absolute;left:0; top:0;">
</div>
<script type="text/javascript">
let divEle = document.getElementById("div");
const distance = 1500; // 需要移动的距离
const timeCount = 3000; // 需要使用的时间
function handler( time ) {
// time为rAF返回的毫秒级时间单位,当time的大于timeCount的值则停止
// time理论上是从 1 开始到timeCount定义的3000,
if(time > timeCount) {
time = timeCount;
}
// 这句代码的作用是 time理论上是从 1 至 3000
// 当到达3000的时候,time * distance / timeCount得到的一定是distance的值1500
divEle.style.left = time * distance / timeCount;
window.requestAnimationFrame( handler ); // 循环调用,渲染完成会停止
}
window.requestAnimationFrame( handler );
</script>
# setTimeOut和setInterval的缺点
- 「造成无用的函数运行开销:」
也就是过度绘制,同时因为更新图像的频率和屏幕的刷新重绘制步调不一致,会产生丢帧,在低性能的显示器动画看起来就会卡顿。
「当网页标签或浏览器置于后台不可见时,仍然会执行,造成资源浪费」
「API本身达不到毫秒级的精确:」
如果使用 setTimeout或者setInterval 那么需要我们制定时间 假设给予 (1000/60)理论上就可以完成60帧速率的动画。所以事实是浏览器可以“强制规定时间间隔的下限(clamping th timeout interval)”,一般浏览器所允许的时间再5-10毫秒,也就是说即使你给了某个小于10的数,可能也要等待10毫秒。
- 「浏览器不能完美执行:」 当动画使用10ms的settimeout绘制动画时,您将看到一个时序不匹配,如下所示。
这种透支会导致动画断断续续,「因为每三帧都会丢失」。计时器分辨率的降低也会对电池寿命产生负面影响,并降低其他应用程序的性能。
← script标签上的属性 排序 →