登录页还在用渐变?我一口气做了 5 个能摸的背景动效:吹蒲公英、滴墨染水、点熔岩闷裂

发布日期:2026-08-31

标签:前端 / React / Canvas / WebGL / 组件库 / cos-design / 视觉特效 / 交互设计

做活动页、登录屏、品牌 Landing 时,背景常常是个尴尬的存在:纯 CSS 渐变太「模板站」,Three.js 流体 Demo 又太重,临时抄一段 CodePen 往往只能撑一次活动。

我在 cos-design 里一直在补这一层------可装包、可调参、能交互的背景动效。v3.6 做了深海气泡场,v3.5 做了水面涟漪和烟雾雾气,v3.7 把图片预览做成了 13 种「能摸」的交互。

v3.8.0 这次一口气上了 5 个重磅背景组件,组件总数来到 91 。它们气质完全不同,但共享同一套工程约定:fill 铺满、bindVisibilityPause 切 tab 暂停、prefers-reduced-motion 静态帧降级、独立 @cos-design/* 分包。

全文约 12 分钟 。建议先打开 Playground(侧栏 → 背景动效),边读边摸------每个组件我都按侧栏顺序写:先讲为什么要做,再拆核心实现,最后留关键交互帧给你对照。

仓库github.com/jiaxiantao/... · 版本cos-design@3.8.0

组件 中文 渲染路线 独立包
SoapBubbles 肥皂泡天空 Canvas 2D 薄膜光学 + 融合 @cos-design/soap-bubbles
DandelionField 蒲公英播种 Canvas 2D 生命周期 + 物理 @cos-design/dandelion-field
LavaBubble 熔岩泡 CPU 场仿真 + WebGL 着色 @cos-design/lava-bubble
InkBloom 墨染清水 Canvas 2D 密度/速度场 @cos-design/ink-bloom
AuroraVeil 极光帷幕 Canvas 2D 光带轮廓 @cos-design/aurora-veil

开场:五个场景,五种「介质」

下面五张图,就是 Playground 里五个新组件的默认态------从肥皂泡到极光,一张比一张「不像 CSS 能做出来的」:

这五个组件不是「五种配色方案」,而是五种介质模型

  • 肥皂泡 --- 薄膜干涉色 + 上升气流 + 融合/爆裂
  • 蒲公英 --- 植株生命周期 + 种子飘飞 + 落地发芽闭环
  • 熔岩 --- 壳层鼓起、闷裂、空腔回填的高度场
  • 墨染 --- 浓墨滴入清水后的密度场流体
  • 极光 --- 垂向光带在星夜里的弯曲与脉冲

对外都是几个 props;对内各自拆模块。接下来五个章节,我会按这个顺序往下走------每节末尾留 Playground 链接,方便你对照动效


一、SoapBubbles:薄膜光学,不是几个半透明圆

v3.8.0 的第一个背景,我从最「轻」的开始:纯 Canvas 2D,没有场仿真,但要骗过眼睛------泡膜得泛虹彩,碰在一起得会融,戳破得溅水珠。

它解决什么问题?

「肥皂泡背景」最容易翻车:画几个 arc + 白高光,看起来像玻璃球。真实肥皂泡的关键是薄膜干涉 ------厚度变化带来彩虹色斑,以及融合时 neck 收缩的 metaball 形态。

核心实现:film + merge + pop 三件套

薄膜色 film.tsfilmRgb(thickness) 在 8 个干涉色 stop 间插值;drawSoapIridescence 在泡内撒 7~12 个 softBlob 色斑 + rim 色带,按 lambertAtAngle 受光。

运动 :每个泡有 rise(上升力)、gust(阵风倍率)、drift(横向目标),用指数 ease 追踪目标速度------不是匀速上升,而是「忽快忽慢、左右晃」。

融合 merge.ts:近距先弱吸引,触及时 30% 概率双爆、70% 进入 merge 状态机。resolveMergePoseapproach / absorb 两阶段,体积按 ∛(r₁³+r₂³) 守恒,绘制时用 metaball 轮廓(与 BubbleField 同思路)。

爆裂 popBubble:14+ 颗 bead 水珠沿点击反方向飞出 + 28+ 颗 mist 微粒;pop 动画结束后 spawnBubble 从底部补新泡。

tsx 复制代码
import { SoapBubbles } from '@cos-design/soap-bubbles';

<SoapBubbles fill count={28} speed={1} />

左:点击戳破,膜 fade 的同时水珠飞出。右:两泡靠近融合,颈部收缩成 metaball 连体。

一句话带走:虹彩来自薄膜厚度插值,灵魂在 merge 状态机------体积守恒 + metaball 绘制。

👉 试玩:#/soapBubbles


二、DandelionField:一整片会呼吸的生命循环

肥皂泡是「死物物理」;第二个组件我想做活物逻辑------不是粒子往上飘,而是一株株蒲公英真的在长高、开花、变绒球、被风吹散、再发芽。Playground 里等 intro 动画播完,你会看到草坡上一片 mature 绒球,那就是最佳阅读起点。

它解决什么问题?

「飘絮粒子」Demo 很多,但很少做成完整生态:发芽 → 开花 → 变绒球 → 被风吹散 → 种子落地 → 再发芽。DandelionField 要的是这个闭环,而且鼠标滑过要像真的在吹风。

核心实现:模块化生命周期

源码按职责拆成六个文件------v3.8.0 CHANGELOG 里专门提到的重构:

bash 复制代码
dandelionField/
├── index.tsx      # React 外壳、主循环、指针风场
├── plant.ts       # 植株状态机:sprout → flower → puffing → mature → wither
├── seed.ts        # 附着/脱离、飘飞、落地、发芽
├── draw.ts        # 茎叶、绒球、种子、光晕
├── scene.ts       # 静态背景 + 草簇风摆
└── frame-cache.ts # 植株几何缓存(每帧不算两次 head 位置)

植株状态机 是灵魂。以 mature 为例:先 matureHoldLeft 随机驻留,到期后 beginReleasing,按 scheduledRelease 时间轴不规则释放种子------外圈先飞、内圈后飞,而不是一次性爆炸。

指针风场pointermove 根据位移速度算 gust,扫过 mature/puffing 绒球且距离 < 105×scale 时,boostPlantRelease 加速散种;点击最近绒球则 boostPlantRelease(2.8) 整朵炸开。

种子发芽 :落地后延迟 germinateDelay,按 GERMINATION_CHANCE / GERMINATION_NEAR_CHANCE 决定是否在附近长出新株------离母株越近概率越高,视觉上像「一片草地慢慢蔓延」。

tsx 复制代码
import { DandelionField } from '@cos-design/dandelion-field';

<DandelionField fill plantCount={10} seedCount={32} speed={1} />

左:快速划过绒球,风场加速散种。右:点击整朵炸开,种子刚离体的一瞬。

性能优化 (v3.8.0 重点):PlantFrameCache 缓存 head/life 几何;AttachedSeedTracker 用计数器替代每帧 filter 附着种子------植株多时帧率更稳。

一句话带走:这不是粒子系统,是带 germination 闭环的植株状态机 + 指针风场。

👉 试玩:#/dandelionField(建议先等 intro 播完再摸)


三、LavaBubble:CPU 算场,GPU 只看

前两个都是 Canvas 2D。第三个我想做「硬介质」------熔岩湖。难点不在颜色渐变,而在完整事件链:鼓起 → 变薄 → 闷裂 → 空腔 → 回填,还要能点击、能拖拽撕裂。这是五个组件里唯一上了 WebGL 的。

它解决什么问题?

熔岩/岩浆视觉在 Web 里常被做成 looping 视频或纯 shader 噪声------能看,但很难点击鼓起 → 壳裂 → 溅射 → 空腔回填 这一完整事件链。LavaBubble 拆成:CPU 192×192 场仿真 写纹理,WebGL 片段着色读高度/热量/空腔做法线光照。

核心实现:Blister 四阶段状态机

每个 blister 经历:

scss 复制代码
inflate(壳层隆起)→ thin(变薄、裂纹发热)→ burst(撕裂空腔 + 溅射)→ cavity(回填、余温)

sim.ts 里维护三张标量场:

  • heightField:隆起高度
  • heatF:热量(写回 shader 做辉度)
  • cavityF:爆裂后的空腔深度

upload() 把 RGBA 纹理打包:R=height, G=heat, B=cavity

片段着色器 shaders.ts 从纹理采样后:

  1. calcNormal 算法线(空腔会「挖深」法线)
  2. heatColor(t) 六段渐变------最低也是暗红,避免纯黑熔壳
  3. wrap lighting + 双层 specular + 空腔 rim 余温

交互两路:

  • 点击 spawnBlister(u,v) 在 UV 坐标鼓新泡
  • 拖拽 applyStir 注入涡旋、螺旋热纹、椭圆 shell crack,并 stirBlister 加速未爆泡的裂纹
tsx 复制代码
import { LavaBubble } from '@cos-design/lava-bubble';

<LavaBubble fill heat={1} autoSpawn activity={1} speed={1} />

左:点击后 burst 相,空腔 + 溅射热纹。右:壳层隆起、裂纹发热的 thin 阶段。

为什么 CPU + GPU 分工? 气泡生命周期、溅射粒子(mist/bomb/crust 三类 spatter)、热扩散需要随机性和分支逻辑,放 CPU 更直观;全屏像素着色交给 GPU,192² 纹理足够表达「湖面的起伏与裂口」。

一句话带走:仿真在 CPU,观感在 GPU------纹理 RGB 三包 height / heat / cavity。

👉 试玩:#/lavaBubbleautoSpawn 开着等几秒,看随机鼓泡)


四、InkBloom:一滴墨,两种场

熔岩是「硬壳 + 碎裂」;第四个我想做完全相反的质感------软、慢、会累积。墨水滴进清水,溶开后整盆水越来越深------这种「越摸越深」的反馈,blur 圆叠不出来。

它解决什么问题?

「墨水扩散」特效常见做法是叠几张 blur 圆------像,但缺少溶开整片水被染深 的累积感。InkBloom 用轻量 2D 流体近似:密度场 + 速度场 + 持久染色场

核心实现:三缓冲区 + 低分辨率仿真

仿真网格 simW × simH ≈ 画布 / 4,每格维护:

变量 作用
密度 dens 当前悬浮的浓/淡墨团
速度 vx, vy 平流 + 涡旋驱动
染色 stain 已溶入水中、几乎不退色的背景色

主循环(每帧):

scss 复制代码
applyForces → 粘滞衰减 → advect(dens) → diffuseDensity
→ advect(velocity) → dens 溶入 stain → stain 缓慢铺匀
→ paintWater(avgStain) → renderStain → renderInk

点击注入 injectDrop 做三件事:不规则浓墨心(fbm 扰动边界)、3~6 个随机涡旋(切向速度)、外圈 22 点淡墨晕。拖拽 injectStir 把指针位移写进速度场。

力场里值得单独说的一句是涡度约束 ------从 curl 场反推侧向推力,拉出絮状细丝,避免每次扩散都是完美圆:

ts 复制代码
const cx = (curl[i + 1] - curl[i - 1]) * 0.5;
const cy = (curl[i + simW] - curl[i - simW]) * 0.5;
vx[i] += (cy / len) * curl[i] * eps;
vy[i] -= (cx / len) * curl[i] * eps;

质量守恒的染色 :浓团 dissolve 后质量转入 stainpaintWater 根据 avgStain 把清水从 #c5dff0 渐变为 #12151a------点得越多,整盆水越深,这是和「一次性 blur 圆」最大的体验差异。

tsx 复制代码
import { InkBloom } from '@cos-design/ink-bloom';

<InkBloom fill inkColor="#0c0e12" speed={1} />

上:单点滴墨,羽状晕开。下:连续多点后,整片清水被染深------注意背景色已从浅蓝变成深灰。

一句话带走dens 是悬浮墨团,stain 是已溶入水的永久染色------质量守恒,越点越深。

👉 试玩:#/inkBloom(连续点 5~8 次,等十秒看背景变深)


五、AuroraVeil:把极光画成「可弯曲的丝带」

五个里最后做、也最「登页面」的一个。极光背景烂大街,但多数要么像壁纸、要么 shader 调参劝退。我想用纯 Canvas 2D 做出光带能被鼠标弯曲、点击能爆发脉冲的北极夜------不引 WebGL,但要够「哇」。

它解决什么问题?

市面上「极光背景」常见两条路:CSS 渐变动画(轻,但像壁纸)或 WebGL 噪声场(真,但调参成本高)。AuroraVeil 选中间态:纯 Canvas 2D ,用逐行轮廓 描述光带,再用离屏 buffer + screen 混合叠出辉光。

核心实现:EdgeProfile 光带

每条光带(Veil)不是一张图片,而是一列 y → 中心 x、半宽 hw 的采样:

ts 复制代码
// 每一行 y:正弦波叠加 drift + 指针磁吸 + 点击脉冲扰动
const centerX = (veil, y, time, sheet) => { /* wave + drift + pointer magnet */ };
const halfWidth = (veil, y, time, sheet) => { /* 指针附近收窄 */ };

// Float32Array 缓存左右边缘,拼成闭合 ribbon
const buildProfile = (veil, time, sheet): EdgeProfile => { /* leftBuf / rightBuf */ };

渲染分三层:

  1. paintSky:深空渐变 + 260 颗按深度闪烁的星 + 地平线暗角
  2. fillRibbon :水平/垂直双色渐变,source-atop 做垂向衰减
  3. 离屏合成 :主 sheet screen 叠一层,再 blur 2.4px 的 glowSheet 做柔辉

交互上,指针会磁吸弯曲 最近光带、收窄 经过区域的半宽;点击触发 burstRef 整屏 ripple + 最多 4 个扩散 pulse。

tsx 复制代码
import { AuroraVeil } from '@cos-design/aurora-veil';

<AuroraVeil fill colors={['#7ee8d8', '#4cc9f0', '#9d8df1']} bandCount={5} speed={1} />

工程细节MAX_DPR = 2prefers-reduced-motion 时 stars 停止 twinkle、光带冻结;与 BubbleField 一样走 @cos-design/shareduseCanvasBox

一句话带走:光带不是贴图,是逐行 EdgeProfile + 离屏 glow 合成。

👉 试玩:#/auroraVeil(在光带中心点一下,看脉冲扩散)


六、五个组件怎么选?一张表说清楚

五个组件读下来,你可能已经在脑子里对上了场景。我把选型收成一张表------按 Playground 侧栏顺序排列,方便回去对照:

你要的气质 推荐组件 交互方式 渲染成本
童趣 / 夏日 / 轻活动 SoapBubbles 点击戳破、自动融合 中(纯 Canvas)
治愈 / 春季活动 / 自然系 DandelionField 滑动吹风、点击炸开 中高(多实体 + 生命周期)
硬核 / 游戏 / 暗黑 Hero LavaBubble 点击鼓泡、拖拽撕裂 中高(WebGL + CPU sim)
国风 / 文艺 / 留白排版 InkBloom 点击滴墨、拖拽搅动 中(低分辨率场)
科技感 / 登录屏 / 北欧夜 AuroraVeil 移动弯曲、点击脉冲 中(离屏 blur)

还有一个 cos-design 一贯建议:一页只放一个强全屏动效背景 。第二个背景请降尺寸或关 interactive,否则抢视觉、也抢 GPU。

如果五个都想试一遍,Playground 侧栏从上往下点就行------和本文顺序完全一致。


七、工程化:分包、降级、Next.js

五个组件均已独立发布,可按需安装------不必为了一个肥皂泡拖进整个库:

bash 复制代码
pnpm add @cos-design/soap-bubbles @cos-design/dandelion-field @cos-design/lava-bubble @cos-design/ink-bloom @cos-design/aurora-veil
# 或全量
pnpm add cos-design@3.8.0

Next.js App Router 记得动态导入 + ssr: false(Canvas/WebGL 仅客户端):

tsx 复制代码
import dynamic from 'next/dynamic';

const SoapBubbles = dynamic(
  () => import('@cos-design/soap-bubbles').then((m) => m.SoapBubbles),
  { ssr: false }
);

export default function Hero() {
  return (
    <section style={{ position: 'relative', minHeight: '100vh' }}>
      <SoapBubbles fill />
      {/* 前景内容 z-index 需高于 canvas */}
    </section>
  );
}

共享约定(v3.7 起逐步统一,五个新组件全部遵循):

  • fill :父级需有明确高度(如 100vh),组件铺满
  • bindVisibilityPause:切 tab 停 rAF,省电
  • prefers-reduced-motion:静态帧,不黑屏
  • Playwright smoke:v3.8.0 为新组件补了 canvas 挂载测试

@cos-design/shared 本版还导出了 softSat 等数学工具,供后续组件复用。


收尾:从 Demo 到「介质库」

回头看 v3.8.0,这五个组件不是简单「+5」,而是在补 cos-design 背景线的介质维度

  • SoapBubbles → 薄膜
  • DandelionField → 生命
  • LavaBubble → 热/壳层
  • InkBloom → 流体
  • AuroraVeil →

它们和早期的 WeatherBackground、BubbleField、RippleWater 一起,构成一套「不用 Three.js 也能做重磅背景」的选项库。

如果你只来得及摸一个:夏日活动页戳 SoapBubbles,登录屏弯曲 AuroraVeil,国风留白滴 InkBloom背景能不能留人,往往差的就是这一层可交互的介质感------读完这篇,打开 Playground 走一遍侧栏,比看十张截图都直观。


链接与延伸阅读

资源 地址
Playground jiaxiantao.xyz/cos-design/...
SoapBubbles jiaxiantao.xyz/cos-design/...
DandelionField jiaxiantao.xyz/cos-design/...
LavaBubble jiaxiantao.xyz/cos-design/...
InkBloom jiaxiantao.xyz/cos-design/...
AuroraVeil jiaxiantao.xyz/cos-design/...
GitHub github.com/jiaxiantao/...
npm www.npmjs.com/package/cos...

同系列文章

相关推荐
陆枫Larry14 分钟前
状态机简介
前端
陆枫Larry1 小时前
一次登录问题排查:接口没错,错的是旧业务逻辑
前端
小小小小宇1 小时前
大模型打分与采样原理,以及 Pi Agent 核心原理
前端
一位正在转型AI全栈的前端工程师1 小时前
AI 全栈学习之旅 -Week 5:RAG 知识库问答系统:从零到生产级部署的全栈实践总结
前端·python
光影少年2 小时前
react navite 安卓iOS 打包、签名、环境区分
前端·react native·react.js
coderCN2 小时前
Nodejs 第三十四章 数据库(表达式和函数、子查询和连表)
前端·node.js
ssshooter2 小时前
AI 时代你不能不知道的 git worktree
前端·后端·面试
观测云3 小时前
AI时代的用户访问监测:观测云带你身临其境体验用户与前端UI交互旅程
前端·可观测性·观测云·rum
喵本喵叁肆3 小时前
06-M6-部门过滤与综合研判-从问答机到研判助手
前端·javascript·jquery