流动极光背景为什么巨卡:别动 background-position,动 transform
很多炫酷的「极光 / 渐变流动」背景(Aceternity 的 aurora-background 是典型)长这样:一层大渐变,套上 filter: blur(30~48px) 加 background-blend-mode,再用 @keyframes 把 background-position 从 50% 推到 350%,让模糊的色块缓缓流动。
效果很美。然后你打开它,hero 卡成幻灯片。
现象
页面一进来就掉帧,滚动、交互全部发涩。但 DOM 不复杂、JS 也没在跑——纯粹是这一层背景动画在拖垮整个页面。
根因:你在让 GPU 每一帧都重做最贵的活
关键在于改了什么属性:
background-position是一个 paint 属性——它不走 GPU 合成(composite),改它会让元素重新光栅化(re-raster)。- 而这一层还套着全屏的
filter: blur()。blur 是最贵的滤镜之一。
两件事叠加:每一帧,浏览器都要把这块全屏的模糊层重新光栅化、重新 blur 一遍。@60/120fps 地全屏重 blur,GPU/CPU 直接顶到天花板。
简单说:你以为只是「挪了一下背景位置」,实际上是在命令浏览器每帧重画 + 重新模糊整个屏幕。
background-position 是 paint 属性(改它就触发重新光栅化),又叠上全屏的 filter: blur()——于是每一帧浏览器都在重画 + 重新模糊整个屏幕。
正解:纹理静态化,动画只走 transform
合成(composite)和绘制(paint)是两条完全不同的路径:
- 合成走 GPU,而且能缓存图层位图。 一层内容只要不变,GPU 把它光栅化一次、缓存成一张合成层位图,之后每帧只是去平移 / 旋转 / 缩放这张位图——纯 composite,不重绘、不重 blur。
- 绘制 / 模糊走光栅化路径,内容一变就得重做。
所以修法是把「会变的东西」从 paint 属性挪到 transform:
- 渐变纹理保持静态,不再动
background-position。 - 动画关键帧从
background-position: from/to改成transform: translate3d() / rotate() / scale(),并加上will-change: transform。
GPU 把这层静态模糊一次性烤成位图缓存住,之后逐帧只是平移这张已经模糊好的位图 → composite-only。
渐变纹理保持静态、不再动 background-position;动画关键帧改成 transform: translate3d() + will-change: transform。GPU 缓存位图后逐帧纯 composite,不重绘、不重 blur。
两个布局上的细节别漏:
- 流动层要放大(
inset-[-30%]+scale > 1),免得 transform 平移时露出边缘。 - 如果有聚焦用的 mask,放在独立的静态外层——否则 mask 会跟着流动层一起转,中心焦点会晃。
实测
hulianui 的 Aurora 背景改完之后,用 RAF 采样帧间隔测:
- 平均 120fps
- 最差帧 10.5ms
- 掉帧(帧间隔 > 32ms)0 个
从「巨卡顿」到「丝滑」,一个属性的事。
一句话外卖
任何「大面积模糊 + 流动」的装饰背景,一律静态纹理 + transform 动画,绝不去动
background-position/background-size这类 paint 属性。
验证流畅度用 RAF 采样帧间隔(> 32ms 即掉帧)来量化。肉眼会骗你,数据不会。