情景重现

在做移动端菜单的时候发现设置 will-change:transform 会导致 position fixed 的子元素相对于父级元素来定位,一个简答的例子

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <title>Document</title>
  <style>
    .container {
      width: 100%;
      height: 100px;
      background: #333;
      will-change: transform;
    }

    .child {
      position: fixed;
      width: 100%;
      height: 100%;
      background-color: rgba(0, 0, 0, 0.3);
    }
  </style>
</head>

<body>
  <div class="container">
    <div class="child"></div>
  </div>
</body>

</html>

运行结果中child元素不是相对于浏览器窗口来定位

原因分析

fixed的定义是生成绝对定位的元素,相对于浏览器窗口进行定位

will-change属性为web开发者提供了一种告知浏览器该元素会有哪些变化的方法,这样浏览器可以在元素属性真正发生变化之前提前做好对应的优化准备工作。这种优化可以将一部分复杂的计算工作提前准备好,使页面的反应更为快速灵敏,虽然好用,但是不要滥用

w3.org有一句话“If any non-initial value of a property would cause the element to generate a containing block for fixed-position elements, specifying that property in will-change must cause the element to generate a containing block for fixed-position elements.”

大致的意思是使用 will-change 会导致元素产生新的层叠容器,为了更好渲染不导致整个页面重绘

但是特殊的是,使用 will-change:opacity 却不会导致 position:fixed 失效,所以导致这个原因应该不是 will-change 属性,应该是 transform如果在父元素使用 transform 属性,也会导致子元素的 position:fixed 失效

最简单的解决方法是,如果使用到transform动画时候,尽量不要在transform的元素上的子元素使用fixed布局或者absolute布局