文章摘要
发布日期: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/… · 版本 : [email protected] 组件 中文 渲染路线 独立包 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.ts : filmRgb(thickness) 在 8 个干涉色 stop 间插值; drawSoapIridescence 在泡内撒 7~12 个 softBlob 色斑 + rim 色带,按 lambertAtAngle 受光。 运动 :每个泡有 rise (上升力)、 gust (阵风倍率)、 drift (横向目标),用指数 ease 追踪目标速度——不是匀速上升,而是「忽快忽慢、左右晃」。 融合 merge.ts :近距先弱吸引,触及时 30% 概率双爆、70% 进入 merge 状态机。 resolveMergePose 分 approach / absorb 两阶段,体积按 ∛(r₁³+r₂³) 守恒,绘制时用 metaball 轮廓(与 BubbleField 同思路)。 爆裂 popBubble :14+ 颗 bead 水珠沿点击反方向飞出 + 28+ 颗 mist 微粒;pop 动画结束后 spawnBubble 从底部补新泡。 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 里专门提到的重构: 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 决定是否在附近长出新株——离母株越近概率越高,视觉上像「一片草地慢慢蔓延」。 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 经历: inflate(壳层隆起)→ thin(变薄、裂纹发热)→ burst(撕裂空腔 + 溅射)→ cavity(回填、余温) sim.ts 里维护三张标量场: heightField :隆起高度 heatF :热量(写回 shader 做辉度) cavityF :爆裂后的空腔深度 upload() 把 RGBA 纹理打包: R=height, G=heat, B=cavity 。 片段着色器 shaders.ts 从纹理采样后: calcNormal 算法线(空腔会「挖深」法线) heatColor(t) 六段渐变—— 最低也是暗红 ,避免纯黑熔壳 wrap lighting + 双层 specular + 空腔 rim 余温 交互两路: 点击 spawnBlister(u,v) 在 UV 坐标鼓新泡 拖拽 applyStir 注入涡旋、螺旋热纹、椭圆 shell crack,并 stirBlister 加速未爆泡的裂纹 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。 👉 试玩: #/lavaBubble ( autoSpawn 开着等几秒,看随机鼓泡) 四、InkBloom:一滴墨,两种场 熔岩是「硬壳 + 碎裂」;第四个我想做完全相反的质感—— 软、慢、会累积 。墨水滴进清水,溶开后整盆水越来越深——这种「越摸越深」的反馈,blur 圆叠不出来。 它解决什么问题? 「墨水扩散」特效常见做法是叠几张 blur 圆——像,但缺少 溶开 和 整片水被染深 的累积感。InkBloom 用轻量 2D 流体近似: 密度场 + 速度场 + 持久染色场 。 核心实现:三缓冲区 + 低分辨率仿真 仿真网格 simW × simH ≈ 画布 / 4 ,每格维护: 场 变量 作用 密度 dens 当前悬浮的浓/淡墨团 速度 vx , vy 平流 + 涡旋驱动 染色 stain 已溶入水中、几乎不退色的背景色 主循环(每帧): applyForces → 粘滞衰减 → advect (dens) → diffuseDensity → advect (velocity) → dens 溶入 stain → stain 缓慢铺匀 → paintWater (avgStain) → renderStain → renderInk 点击注入 injectDrop 做三件事:不规则浓墨心(fbm 扰动边界)、3~6 个随机涡旋(切向速度)、外圈 22 点淡墨晕。 拖拽 injectStir 把指针位移写进速度场。 力场里值得单独说的一句是 涡度约束 ——从 curl 场反推侧向推力,拉出絮状细丝,避免每次扩散都是完美圆: 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 后质量转入 stain , paintWater 根据 avgStain 把清水从 #c5dff0 渐变为 #12151a ——点得越多,整盆水越深,这是和「一次性 blur 圆」最大的体验差异。 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 的采样: // 每一行 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 */ }; 渲染分三层: paintSky :深空渐变 + 260 颗按深度闪烁的星 + 地平线暗角 fillRibbon :水平/垂直双色渐变, source-atop 做垂向衰减 离屏合成 :主 sheet screen 叠一层,再 blur 2.4px 的 glowSheet 做柔辉 交互上,指针会 磁吸弯曲 最近光带、 收窄 经过区域的半宽;点击触发 burstRef 整屏 ripple + 最多 4 个扩散 pulse。 import { AuroraVeil } from '@cos-design/aurora-veil' ; < AuroraVeil fill colors = {[ '# 7ee8d8 ', '# 4cc9f0 ', '# 9d8df1 ']} bandCount = {5} speed = {1} /> 工程细节 : MAX_DPR = 2 ; prefers-reduced-motion 时 stars 停止 twinkle、光带冻结;与 BubbleField 一样走 @cos-design/shared 的 useCanvasBox 。 一句话带走 :光带不是贴图,是逐行 EdgeProfile + 离屏 glow 合成。 👉 试玩: #/auroraVeil (在光带中心点一下,看脉冲扩散) 六、五个组件怎么选?一张表说清楚 五个组件读下来,你可能已经在脑子里对上了场景。我把选型收成一张表—— 按 Playground 侧栏顺序排列 ,方便回去对照: 你要的气质 推荐组件 交互方式 渲染成本 童趣 / 夏日 / 轻活动 SoapBubbles 点击戳破、自动融合 中(纯 Canvas) 治愈 / 春季活动 / 自然系 DandelionField 滑动吹风、点击炸开 中高(多实体 + 生命周期) 硬核 / 游戏 / 暗黑 Hero LavaBubble 点击鼓泡、拖拽撕裂 中高(WebGL + CPU sim) 国风 / 文艺 / 留白排版 InkBloom 点击滴墨、拖拽搅动 中(低分辨率场) 科技感 / 登录屏 / 北欧夜 AuroraVeil 移动弯曲、点击脉冲 中(离屏 blur) 还有一个 cos-design 一贯建议: 一页只放一个强全屏动效背景 。第二个背景请降尺寸或关 interactive ,否则抢视觉、也抢 GPU。 如果五个都想试一遍,Playground 侧栏从上往下点就行——和本文顺序完全一致。 七、工程化:分包、降级、Next.js 五个组件均已独立发布,可按需安装——不必为了一个肥皂泡拖进整个库: pnpm add @cos-design/soap-bubbles @cos-design/dandelion-field @cos-design/lava-bubble @cos-design/ink-bloom @cos-design/aurora-veil # 或全量 pnpm add [email protected] Next.js App Router 记得动态导入 + ssr: false (Canvas/WebGL 仅客户端): 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… 同系列文章 BubbleField:用 Canvas 做一个「会呼吸」的深海气泡场 RippleWater & SmokeFog:水面涟漪与烟雾雾气怎么做 WeatherBackground:用 Canvas 做一个「会变天」的背景引擎 cos-design v3.0:从 15 个 Demo 到 49 个组件的视觉特效库