1. 原因
  2. 什么是"containing block"?
  3. 解决办法
  4. 参考

transform导致fixed失效

transform是个超级常用的属性, 需要居中/动画/开启cpu加速时经常会用到.
但这个属性也有很多副作用, 例如把后边的元素盖住了,或者是后代absolute元素被overflow:hidden剪裁.
除此之外还有个影响, 让拥有固定定位(fixed)属性的子元素变的像个绝对定位(absolute)元素.

1.PNG

如上图所示, 黑色子元素为固定定位元素(fixed), 由于黄色父元素transfrom属性的影响, 表现的像个绝对定位元素(absolute).

示例代码:

1
2
3
<div class="wrapper">
<div class="target"></div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.wrapper {
width: 200px;
height: 200px;
background: yellow;
transform: translate3d(0,0,0);
}

.target {
position: fixed;
top: 50%;
width: 30px;
height: 30px;
background: #000;
}

原因

根据css规范:

1
2
3
4
In the HTML namespace, any value other than none for the transform results in the creation of both a stacking context and a containing block. The object acts as a containing block for fixed positioned descendants.
在HTML命名空间(namespace)中,
拥有属性transform的元素(除了transform: none)的元素会创建"层叠上下文"(stacking context)和"包含块"(containing block),
该元素将作为(拥有position: fixed属性的)后代元素的"包含块"

也就是说,如果fixed元素的祖先有transform属性,则fixed元素会相对与这个祖先计算,而不是视口.

什么是"containing block"?

通常情况下(position: static/relative),包含块指的是距离元素最近的"祖先元素的内容区",也就是父元素.
如果position属性值为absolute, 则将"距离该元素最近"且"position属性值不为none"的祖先元素作为包含块.
如果position属性值为fixed, 则将"视口"(viewport)作为包含块.

除此之外, 如果祖先元素拥有下列属性,也可能被作为absolute和fixed的包含块.

  • transform/perspective属性值不为none
  • will-change属性值为transform/perspective

解决办法

不要把固定定位元素放在拥有transform/will-change属性值的元素里面.
导航栏之类的元素也可以用sticky代替fixed,不过sticky的兼容性不太好.
另外,很多动画库都会使用transform来优化动画效果, 如果fixed元素出了什么奇怪的bug,那很可能是这些库搞得鬼.

参考

CSS3 transform对普通元素的N多渲染影响 « 张鑫旭-鑫空间-鑫生活
css - 'transform3d' not working with position: fixed children - Stack Overflow