如何引起页面回流reflow
- 页面初次渲染,这是不可避免的一次回流;
- 添加或删除可见的DOM元素,改变了DOM树的结构;
- 元素的位置、尺寸、边距、内边距、边框等几何属性发生变化,改变了元素的布局;
- 元素的内容发生变化,例如文字数量、字体、图片大小等,改变了元素的大小;
- 元素字体大小发生变化,影响了元素的高度和宽度;
- 改变浏览器窗口尺寸,例如resize事件发生时,影响了视口的大小;
- 激活CSS伪类,例如:hover,改变了元素的样式;
- 设置style属性的值,改变了元素的外观;
- 查询或调用某些属性或方法,例如offsetWidth, offsetHeight, getComputedStyle, getBoundingClientRect等,因为浏览器为了保证返回值的准确性,会强制刷新渲染队列。
通常在DOM操作中,最常用造成回流的就是获取元素的矩形属性
如:offsetWidth, getBoundingClientRect, getComputedStyle等。
这次就用这些来展示效果。
<style>
.container {
height: 180px;
width: 180px;
}
.move {
height: 100px;
width: 100px;
background-color: lightcoral;
transition: 1s;
}
.show {
transform: translate(100px, 100px);
opacity: 1;
}
.hide {
opacity: 0;
transform: translate(0, 0);
}
</style>
<div class="container">
<div class="move"></div>
</div>
<button onclick="toggle()">Click</button>
let isHide = true;
const el = document.querySelector('.move');
function toggle() {
isHide = !isHide;
// 如果`isHide` 说明要隐藏了
// 如果`!isHide` 说明要从隐藏状态切换回来
if (isHide) {
el.className = 'move hide';
el.style.display = 'none';
}
else {
el.style.display = 'block';
// 切换隐藏状态后 立刻触发一次回流`reflow` 可以固定住元素位置
// 然后再设置样式 就能做到动画效果
el.getBoundingClientRect();
el.className = 'move show';
}
}
首先,给按钮绑定一个切换事件,isHide控制展示。
来看效果
乂!!我退出动画呢??
很多人可能有这种烦恼:
我怎么让元素消失的时候有退出动画呢?
- 用
settimeout等动画播放完再隐藏吗?- 不不不,年轻人,这好吗?这不好。
- 首先定时器不准,为什么呢??
- 定时器当嵌套超过4层时,最小延迟时间是4ms,
MDN说的。- 定时器是宏任务,受到同步任务影响,当你同步任务没有执行完毕,定时器就不会执行
那怎么办呀~ 那你能帮帮我吗
这时我们就可以监听动画结束,然后触发相应的事件了,小改造一下
function toggle() {
isHide = !isHide;
// 如果`isHide` 说明要隐藏了 监听动画结束后隐藏元素 再取消事件
// 如果`!isHide` 说明要从隐藏状态切换回来
if (isHide) {
el.addEventListener('transitionend', () => {
el.style.display = 'none';
}, { 'once': true });
el.className = 'move hide';
el.style.display = 'none';
}
else {
el.style.display = 'block';
el.className = 'move show';
}
}
这是加了过渡动画监听的动画 来看效果
啊?!!发生肾麽事了?加了过渡怎么所有动画都没了呢?
这是因为,浏览器不会一句一句去执行你的代码,而是把这些能够合并的,放入一个任务队列里,最后一起执行。
所以这里最终show的样式,并没有动画,只应用了transform属性
那么我该怎么做呢?
这时开头主角就出场了,我们手动执行一次回流,让他分别应用上这俩样式,就能实现了。
function toggle() {
isHide = !isHide;
// 如果`isHide` 说明要隐藏了 监听动画结束后隐藏元素 再取消事件
// 如果`!isHide` 说明要从隐藏状态切换回来
if (isHide) {
el.addEventListener('transitionend', () => {
el.style.display = 'none';
}, { 'once': true });
el.className = 'move hide';
el.style.display = 'none';
}
else {
el.style.display = 'block';
// 切换隐藏状态后 立刻触发一次回流`reflow` 可以固定住元素位置
// 然后再设置样式 就能做到动画效果
el.getBoundingClientRect();
el.className = 'move show';
}
}
湿滑