图像与设计
需要给网页内容后方添加柔和、无缝循环且不抢焦点的动态背景时…
适用于 hero、登录/启动页、空状态或区块背后需要环境感动态背景的场景。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
需要给网页内容后方添加柔和、无缝循环且不通用
解决思路
- 选型:柔和动态渐变/网格用 CSS gradients + keyframes,成本最低且无需 JS。
- 选型:流动极光/有机噪声用 GLSL fragment shader(Three.js full-screen quad),GPU 成本中等。
- 选型:星座/漂移点用 Canvas 2D particles,CPU 成本随数量增长。
- 选型:深度/视差用同一 shader/particles 加鼠标驱动偏移。
- 默认:渐变足够时用 CSS;有机流动颜色用 shader;星座/网络外观用 canvas 粒子。
- 设计:保持 subtle,低对比、慢速(长周期,8–30s 循环),不与文字竞争。
- 设计:保持 readable,为前景文字维持对比度;必要时加 scrim(linear-gradient 覆盖层或 backdrop)。
- 设计:无缝循环,用周期函数驱动,使时间可回绕且无可见跳变。
- 设计:尊重运动偏好和电量,遵循 prefers-reduced-motion,并在离屏或标签隐藏时暂停。
- CSS mesh:背景层使用 position: fixed; inset: 0; z-index: -1。
- CSS mesh:使用多层 radial-gradient 叠加底色,例如 #5b8cff55、#b05bff55、#2de1c255 和 #0b0e1a。
- CSS mesh:设置 background-size: 200% 200%。
- CSS mesh:设置 animation: meshmove 24s ease-in-out infinite。
- CSS mesh:在 @keyframes meshmove 中让 0%, 100% 的 background-position 完全相同,50% 改变位置。
- CSS mesh:在 @media (prefers-reduced-motion: reduce) 中设置 .bg { animation: none; }。
- GLSL shader:创建 THREE.WebGLRenderer({canvas, antialias: true})。
- GLSL shader:用 renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) 限制性能。
- GLSL shader:创建 scene 和 THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1)。
- GLSL shader:定义 uniforms:uTime、uRes、uColorA、uColorB。
- GLSL shader:用 ShaderMaterial,vertexShader 设置 gl_Position = vec4(position, 1.0)。
- GLSL shader:fragmentShader 包含 hash、value noise、fbm 函数。
- GLSL shader:在 fragment 中用 gl_FragCoord.xy / uRes.xy 得到 uv,用 fbm(uv*3.0 + vec2(uTime*0.05, uTime*0.03)),再用 smoothstep(0.2, 0.8, n + uv.y*0.3) 混合 uColorA 与 uColorB。
- GLSL shader:用 new THREE.PlaneGeometry(2, 2) 和 material 创建 quad,并 scene.add(quad)。
- GLSL shader:resize 时调用 renderer.setSize(window.innerWidth, window.innerHeight) 并更新 uniforms.uRes.value.set(window.innerWidth, window.innerHeight);监听 resize 并初始化。
- GLSL shader:使用 THREE.Clock;在 rAF loop 中 running 时设置 uniforms.uTime.value = clock.getElapsedTime(),调用 renderer.render(scene, camera),然后 requestAnimationFrame(loop)。
- Canvas particles:获取 canvas 2D context;设置 W、H、pts;COUNT = 80,LINK = 120。
- Canvas particles:init 时设置 canvas.width = innerWidth、canvas.height = innerHeight;生成 COUNT 个点,随机 x、y 和速度 (Math.random()-0.5)*0.3。
- Canvas particles:监听 resize 调用 init,并初始化。
- Canvas particles:frame 中调用 ctx.clearRect(0, 0, W, H)。
- Canvas particles:对每个点执行 p.x += p.vx、p.y += p.vy;越界时反转 vx/vy;用 ctx.fillStyle = '#9db4ff' 和 ctx.fillRect(p.x, p.y, 2, 2) 画点。
- Canvas particles:双循环 i<j<COUNT,计算 dx、dy、d=Math.hypot(dx, dy);若 d<LINK,用 ctx.strokeStyle = `rgba(157,180,255,${1-d/LINK})` 画线。
- Canvas particles:调用 requestAnimationFrame(frame)。
- Canvas particles:O(n²) 连接循环在约 120 个点以内可行;超过则用 spatial-hash 分网格,只测试相邻单元。
- 无缝循环:canvas/JS 中用固定 PERIOD 秒,计算 phase = (t % PERIOD) / PERIOD * Math.PI * 2。
- 无缝循环:用 offset = Math.sin(phase) * amp;t = PERIOD 时回到起点。
- 无缝循环:任何仅由 phase 的 sin/cos(或整数倍)构建的运动可无缝循环。
- 无缝循环:shader 中令 uTime = phase 并只用其 sin/cos;噪声滚动渐变时按每周期滚动整数个噪声单元使场平铺。
- Reduced motion:检测 matchMedia('(prefers-reduced-motion: reduce)').matches;若 reduce,设 running = false,然后 renderer.render(scene, camera) 渲染一帧静态后停止。
- 隐藏暂停:document.addEventListener('visibilitychange', () => { running = !document.hidden && !reduce; })。
- 离屏暂停:new IntersectionObserver(([e]) => { running = e.isIntersecting && !document.hidden && !reduce; }).observe(canvas)。
- 暂停实现:running 为 false 时跳过 rAF 循环中的 render;保持循环存活以便低成本恢复。
- 交付:作为可直接在浏览器打开的单个 .html 文件;markup、背景、shader/canvas 时的 Three.js 或 canvas JS 从 CDN 全部内联。
- 输出契约:一个 .html 文件包含背景层 + 前景文字样例,以检查可读性/对比度。
- 输出契约:只用一个动画驱动:CSS @keyframes、shader rAF 循环或 canvas rAF 循环之一。
- 输出契约:包含与所选技术匹配的 freeze harness,使任意时刻可确定性截图。
- Freeze harness:读取 location.search 的 URLSearchParams 中的 t;若 t !== null,parseFloat 成 T。
- Freeze harness CSS mesh:对 .bg 元素设置 el.style.animationDelay = (-T) + 's',el.style.animationPlayState = 'paused'。
- Freeze harness GLSL:uniforms.uTime.value = T;renderer.render(scene, camera);running = false。
- Freeze harness Canvas:确定性种子,步进模拟到 T,绘制一次并停止循环。
- Freeze harness:设置 window.__ready = true 作为 headless 等待的 ready signal。
- 验证:打开文件冻结在开始/中/结束:?t=0、?t=<period/2>、?t=<period>。
- 验证:对每个冻结帧截图。
- 验证:检查 fidelity(subtle、on-brand、seamless)和 artifacts——每帧文字对比度成立、渐变/shader 无 banding、loop seam(t=0 vs t=period)匹配、无 GPU/console 错误。
- 验证命令:npx playwright screenshot --wait-for-timeout=600 "file://$PWD/bg.html?t=12" frame-mid.png。
- 打包辅助:scripts/seek-shot.sh anim.html 0 1.5 3 冻结 ?t=N harness 并分别截图;scripts/contact-sheet.sh sheet.png frame-*.png 平铺供一眼审查;见 scripts/README.md。
- 完成前:在浏览器独立打开无 console/WebGL 错误、无缺失 CDN。
- 完成前:只有一个驱动;?t=N 冻结精确帧(shader 渲染一帧,canvas 模拟确定性步进)。
- 完成前:在开始/中/结束截图——前景文字可读、无 banding、循环接缝不可见。
- 完成前:遵循 prefers-reduced-motion(渲染一帧静态并停止循环)+ 离屏/隐藏暂停。
- 完成前:devicePixelRatio 限制为 2;运动慢且 subtle,绝不超过内容对比度。
- Quick reference:最便宜渐变用 CSS 多层 radial-gradient + background-position keyframes。
- Quick reference:有机流动用 GLSL fbm noise,滚动 uTime。
- Quick reference:网络/点用 canvas particles + distance-linked lines。
- Quick reference:无接缝用 sin/cos(phase) 驱动运动,首尾关键帧相同。
- Quick reference:性能限制用 setPixelRatio(min(dpr, 2))、throttle、移动端减少粒子数。
- Quick reference:无障碍用 prefers-reduced-motion 静态回退。
- Quick reference:省电用 visibilitychange + IntersectionObserver 暂停。
- Gotchas:绝不让背景对比度超过前景文字——若超过加 scrim。
- Gotchas:Retina/4K 未限制 devicePixelRatio 会拖垮 GPU;限制为 2。
- Gotchas:background-position 只有在首尾关键帧完全匹配时才循环。
- Gotchas:噪声滚动 shader 只有在每周期滚动整数个单元时才循环,否则循环明显跳变。
- Gotchas:忘记离屏/隐藏暂停会在移动端耗电,即使用户看不见。
- 参考:references/background-recipes.md 提供更完整 drop-in 实现:更丰富多 blob CSS mesh 带 blur、完整 Three.js aurora shader 带鼠标视差与真正无缝循环时间回绕、网格优化星座场带鼠标排斥、完美循环噪声流技术、完整 reduced-motion + offscreen-pause 管理器包装全部。
适用边界 · 注意事项
- 需要背景本身高对比、强烈视觉冲击或抢焦点时不要用,因为目标是与内容保持低对比和低调。
- 无法运行 JavaScript/WebGL 或无法加载 Three.js CDN 且没有静态回退时不要用 shader/canvas 方案。
- 星座/粒子外观若需要远超约 120 个点却未实现 spatial-hash 等优化时不要直接用示例 O(n²) 循环。