Chrome中CSS animation导致filter滤镜过渡失效问题求解
问题现象
页面内设置了横向滚动动画的链接元素,默认应用grayscale灰度滤镜,预期鼠标悬浮时通过平滑过渡恢复彩色显示,但在最新版Chrome中出现如下异常:
- 首次悬浮带动画的元素时,
filter属性的transition过渡完全失效,直接瞬间跳转到无灰度状态,即使将悬浮态样式从filter: none改为filter: grayscale(0)也无法修复 - 首次失效后短时间内反复移入移出鼠标,过渡效果会暂时恢复;如果鼠标移出时间较长,或是切换悬浮其他带动画的元素,过渡会再次失效
- 同一代码在Firefox、Brave浏览器中运行正常,旧版Chrome也无此问题,属于Chrome新版本引入的渲染层缓存Bug
最小复现Demo
样式代码
.thing { background-color: red; width: 50px; height: 50px; filter: grayscale(1); transition: filter 1s; } .thing:hover { filter: none; } .broken { animation: move linear 10s infinite; } @keyframes move { 0% { transform: translateX(0%); } 50% { transform: translateX(500%); } }
结构代码
<div class="broken thing">TEST1</div> <div class="broken thing">TEST2</div> <br> <div class="thing">noanim</div> <div class="thing">noanim</div>
上述代码中,不带broken动画类的元素悬浮过渡完全正常,带动画的元素会复现该Bug。
修复方案
该Bug的核心原因是Chrome对运行中transform动画的元素,没有正确缓存filter属性的初始过渡值,首次触发状态变更时直接跳变。可通过以下任意一种方案规避:
- 强制为元素创建独立渲染层
给带动画的元素添加will-change: filter声明,提示浏览器提前分配渲染资源、缓存filter属性状态,即可恢复正常过渡:
如果不想使用.broken { will-change: filter; animation: move linear 10s infinite; }will-change,也可以通过transform: translateZ(0)开启硬件加速达到同样效果,但要注意避免页面过多独立层带来的性能损耗。 - 拆分动画与滤镜的挂载节点
不要把动画和filter属性加在同一个元素上:外层元素负责横向滚动动画,内层子元素负责灰度滤镜和过渡效果,从结构上绕开Chrome的渲染判断逻辑:<!-- 结构调整 --> <div class="broken"> <div class="thing">TEST1</div> </div>/* 样式拆分 */ .broken { animation: move linear 10s infinite; } .thing { width: 50px; height: 50px; background-color: red; filter: grayscale(1); transition: filter 1s; } .thing:hover { filter: grayscale(0); }
两种方案在当前最新版Chrome中测试均有效,不会影响其他浏览器的正常表现。
内容的提问来源于stack exchange,提问作者Stefan Todoran
相关产品推荐
相关产品推荐

