MDN解释
window.requestAnimationFrame()
告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
执行时机W3C建议,回调函数执行次数与浏览器屏幕刷新次数相匹配, 如60Hz刷新,大约就是16毫秒执行一次
参数
回调函数会被传入DOMHighResTimeStamp
参数,DOMHighResTimeStamp
指示由RequestAnimationFrame()排队的回调开始触发的时间。指示当前被 requestAnimationFrame()
排序的回调函数被触发的时间。
回调函数自动传入的参数,可以看做是页面加载后的计时(毫秒数, 如果设备支持,可能精确到微秒)即时间起源 The time origin
在时间原点是被认为是当前文档的生命周期的开始的标准时间。它的计算如下:
- 如果脚本的全局对象是
Window
,则时间原点确定如下:- 如果当前
Document
是第一个加载的Window
,则时间原点是创建浏览器上下文的时间。 - 如果在卸载窗口中加载的上一个文档的过程中,显示确认对话框让用户确认是否离开上一页,时间原点是用户确认导航到该页面的时间。新页面是可以接受的。
- 如果上述两者均未确定时间原点,则时间原点是负责创建窗口当前的导航
Document
发生的时间。
- 如果当前
- 如果脚本的全局对象是
WorkerGlobalScope
(即脚本作为Web worker运行),则时间原点是创建worker的时刻。 - 在所有其他情况下,时间原点未定义。
在同一个帧中的多个回调函数,它们每一个都会接受到一个相同的时间戳,即使在计算上一个回调函数的工作负载期间已经消耗了一些时间。该时间戳是一个十进制数,单位毫秒,最小精度为1ms(1000μs)。
返回值
一个 long
整数,请求 ID ,是回调列表中唯一的标识。是个非零值,没别的意义。可以传这个值给 window.cancelAnimationFrame()
以取消回调函数。
应用
比如在一个页面中插入10万条数据,不考虑滚动加载、分页之类的话,就可以使用 requestAnimationFrame 来改善
<body>
<button>关闭</button>
<div></div>
</body>
</html>
<script>
let timer = null
document.querySelector('button').onclick = function (param) {
window.cancelAnimationFrame(timer)
}
setTimeout(() => {
const num = 100000
const step = 100 // 每次插入100条
const loopCount = num / step
let countOfRender = 0
let div = document.querySelector('div')
function add(){
const fragment = document.createDocumentFragment()
for(let i = 0; i < step; i++){
const span = document.createElement('span')
span.innerText = Math.floor(Math.random() * num)
span.style.color = `hsl(${countOfRender}, 64%, 51%)`
fragment.appendChild(span)
}
div.appendChild(fragment)
// const first = div.firstChild
// div.insertBefore(fragment, first)
countOfRender++
loop()
}
function loop(){
if(countOfRender < loopCount){
timer = window.requestAnimationFrame(add)
}else{
alert('结束了')
}
}
loop()
}, 1000)
</script>
如果一次性插入100000条,页面会卡顿4-8秒左右才渲染完成,使用上述方式插入的话,后期的回流压力也会越来越大,不过前期页面的渲染时间是正常的。