关于如何触发浏览器重绘的一些尝试。
November 23, 2018 • ☕️ 2 min read
我们动态的往body节点上挂一个小球 并且更改它的top值 让它触发动画 (以下代码均在chrome控制台执行)
var div = document.createElement('div')
div.style.cssText = 'position: fixed;width: 30px; height: 30px; left: 0; top: 0; background: red;transition: all 1s'
document.body.appendChild(div)
div.style.top = '500px'
这样写是没用的,因为浏览器很聪明,它会把你在一次task中的样式更改收集起来,再执行渲染的时候再把它一次性改变,但是网上很多人说getComputedStyle这个api可以直接触发重绘,那么我们来试试
var div = document.createElement('div')
div.style.cssText = 'position: fixed;width: 30px; height: 30px; left: 0; top: 0; background: red;transition: all 1s'
document.body.appendChild(div)
// 想触发重绘
getComputedStyle(div)
div.style.top = '500px'
按理说应该会执行动画吧, 可是并没有,这很让人疑惑,我们再这样试试
var div = document.createElement('div')
div.style.cssText = 'position: fixed;width: 30px; height: 30px; left: 0; top: 0; background: red;transition: all 1s'
document.body.appendChild(div)
// 想触发重绘
getComputedStyle(div).top
div.style.top = '500px'
咦,这次终于执行动画了,看来浏览器优化程度到了这一步,就算你去getComputedStyle 它也会惰性的给你返回一个对象, 等到你真正的去读取里面的样式值,才会触发重绘。
再来一个小实验,我们想要让浏览器闪烁两个颜色
document.body.style.background = 'blue'
document.body.style.background = 'red'
显而易见这样是没用的, 那么我们用setTimeout去让中间经历一次浏览器渲染
document.body.style.background = 'blue'
setTimeout(() => { document.body.style.background = 'red' })
这次好像可以了 把这段代码在浏览器里执行多次, 会发现有时候还是会直接变成红色背景,这是为什么呢? 我们继续做个试验
document.body.style.background = 'blue'
setTimeout(() => { document.body.style.background = 'red' }, 16.7)
这段代码再执行n次, 这下每次都会闪烁两种颜色了, 16.7是个什么数字呢,我们一般电脑的屏幕刷新率是60hz,也就是每秒更新60次视图,1000ms / 60 ≈ 16.7 浏览器会根据你的屏幕刷新率去约束渲染线程的执行,去除掉多余无效的渲染。
那牵扯到硬件,假如我们的刷新率只有30hz呢, 或者更多,更少呢? 这也就是为什么浏览器给我们提供了一个api叫requestAnimationFrame,不懂的朋友们可以去查阅一下这个api的用法。 真正保证屏幕一定会闪烁两次的做法
requestAnimationFrame(() => {
document.body.style.background = 'red'
requestAnimationFrame(() => {
document.body.style.background = 'blue'
})
})