返回博客

流动极光背景为什么巨卡:别动 background-position,动 transform

很多炫酷的「极光 / 渐变流动」背景(Aceternity 的 aurora-background 是典型)长这样:一层大渐变,套上 filter: blur(30~48px)background-blend-mode,再用 @keyframesbackground-position50% 推到 350%,让模糊的色块缓缓流动。

效果很美。然后你打开它,hero 卡成幻灯片。

现象

页面一进来就掉帧,滚动、交互全部发涩。但 DOM 不复杂、JS 也没在跑——纯粹是这一层背景动画在拖垮整个页面。

根因:你在让 GPU 每一帧都重做最贵的活

关键在于改了什么属性

  • background-position 是一个 paint 属性——它不走 GPU 合成(composite),改它会让元素重新光栅化(re-raster)
  • 而这一层还套着全屏的 filter: blur()。blur 是最贵的滤镜之一。

两件事叠加:每一帧,浏览器都要把这块全屏的模糊层重新光栅化、重新 blur 一遍。@60/120fps 地全屏重 blur,GPU/CPU 直接顶到天花板。

简单说:你以为只是「挪了一下背景位置」,实际上是在命令浏览器每帧重画 + 重新模糊整个屏幕

坑:paint 属性 + 全屏 blur

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。

正解:静态纹理 + transform 动画

渐变纹理保持静态、不再动 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 即掉帧)来量化。肉眼会骗你,数据不会。