Skip to content

一文搞懂回流重绘以及隐藏元素策略

.display、visibility、opacity 的详细区别以及回流、重绘

1.三者详细区别

表格表示:体现在占据空间、事件出发、覆盖子元素上面

注意比较特殊的地方 opacity:0 是隐藏了依然可以触发事件的!(因为只是透明了,透明的也照样可以点击)

image-20230809220220295

2.重绘和回流(重排)

重绘(Repaint):重绘指的是当元素的样式发生改变,但并不影响其在页面布局中的位置时,浏览器会重新绘制该元素的外观,以显示新的样式。这是一种相对较轻量级的操作,不会影响到元素的布局和其他元素的位置。

回流(Reflow):回流则更为复杂,它指的是当 DOM 结构中的元素发生变化,影响了页面的布局,需要浏览器重新计算元素的几何属性(如位置、尺寸大小等),重新布局整个页面。回流比重绘要消耗更多的资源,因为它涉及到页面的结构变化和重新计算。

回流又名 重布局重排

每个页面在第一次加载时也会回流。

注意:回流必将引起重绘,而重绘不一定会引起回流

总结:只有 display 会引起回流,因为它造成了页面空间的损失!

而重绘是都会造成的,因为 css 属性改变了(不影响布局的属性)!——> opacity 结合 transition 的时候可以不重绘!

image-20230809220845742

注意:opacity 属性比较适合做渐隐渐显效果!因为可以不引起回流和重排。

3.合成层性质实现渐隐渐显效果

渐隐渐显效果:利用合成层性质 + opacity

**合成层概念:**页面的绘制并不一定为在内存中的单层画面绘制,有时浏览器会将一帧画面绘制为多层画面,然后将若干层画面合并成一张图片显示到屏幕上。——> 说白了就是页面具有很多的层次,就是合成层

将页面分层可以更好的区分开页面中静态部分和动态部分的绘制。

在 Blink 和 WebKit 内核的浏览器中,具有 transition 或 animation 的 opacity 元素,渲染层被提升为合成层

translateZ(0) 或 translate3d(0,0,0)可以人为强制创建合成层。

而元素提升为合成层后,transform 和 opacity 不会触发 重绘制。如果不是合成层,则会触发重绘。

因为透明度改变后,GPU 在绘制页面时可以改变之前画好的页面的透明值,而无需整体的重绘。 但是这个被修改的 opacity 必须为一个单独的图层,否则图层中的其他节点也会被重绘。

1.使用 opacity 实现

逐渐过度的渐隐渐显

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <style>
      .blue {
        width: 200px;
        height: 200px;
        background: blue;
        transition: 1s; /* 1s过渡 */
      }
    </style>
  </head>
  <body>
    <div class="blue" id="target"></div>
    <script>
      var flag = false;
      setInterval(function () {
        //使用定时器实现样式切换,也可以使用事件
        flag = !flag;
        target.style.opacity = flag ? 0 : 1;
      }, 1000);
    </script>
  </body>
</html>

使用事件优化:

css
.blue {
  width: 200px;
  height: 200px;
  background: blue;
  transition: 1s;
  /* 1s过渡 */
  opacity: 1;
}

.blue:hover {
  opacity: 0;
}

2.使用 visibility 实现 ——> 效果不好,不能平滑实现,不能渐变,是突变的

离散效果的延迟闪烁

可以将 visibility : hidden 看成 visibility : 0; 并且将 visibility : visible 看成 visibility : 1;

visibility 的值大于 0 时,元素就被显示。所以尽管 visibility 的值是从 0 到 1 平滑过度的,但是展示效果并不平滑。

在鼠标移进元素范围内的一秒后,visibility 的值才为 0(突然一下变了),元素的隐藏效果被延迟。

而元素再次移动后,visibility 的值迅速变为一个大于 0 的值,元素的展现效果不延迟(因为元素 visibility : hidden 时,监听的事件不会被触发,那么 transition 也不会起到效果,只是会根据 hover 伪类状态的失去而重新显示元素。所以当设置 visibility : visible 时,不可以把事件直接绑定在 hidden 的元素上,可以考虑在旁边新建一个元素并绑定事件。)

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <style>
      .blue {
        width: 200px;
        height: 200px;
        background: blue;
        transition: 1s; /*作用于hover事件,只有变成hidden的时候生效*/
        visibility: visible;
      }
      .blue:hover {
        visibility: hidden;
      }
    </style>
  </head>
  <body>
    <div class="blue"></div>
  </body>
</html>

优化:可以实现鼠标放在红块上 1s 后蓝块消失,离开红块后 1s 蓝块显示(但是也是线性的,不能渐变)

html
<head>
  <meta charset="UTF-8" />
  <style>
    .red {
      width: 200px;
      height: 200px;
      background: red;
    }

    .blue {
      width: 200px;
      height: 200px;
      background: blue;
      transition: 1s;
      visibility: hidden;
    }

    .red:hover + .blue {
      visibility: visible;
    }
  </style>
</head>

<body>
  <div class="red"></div>
  <div class="blue"></div>
</body>

3.使用 display 无法实现

display 不支持 transition,且会使 transition 失效。

因为 display:none 的元素不会被渲染在页面上

但是 transition 只对已经渲染在页面上的元素起做作用。