关于 scroll-and-webgl
面向营销页面滚动特效、WebGL 与 Canvas 场景的设计技能,提供注重性能和无障碍的预算与实现指导。适合制作表现力丰富网页,同时确保脚本运行前文本可见,并提供减少动态效果及无 JavaScript 回退的开发者。
主要功能
- 01按复杂度划分滚动动画方案
- 02滚动特效的性能预算
- 03文本显现与无障碍指导
- 04Canvas 和 WebGL 资源管理
- 05延迟加载与加载顺序
- 06减少动态效果及无 JavaScript 回退
适用场景
- 01为营销页面添加滚动显现效果
- 02构建固定或滚动联动场景
- 03创建带静态回退的 WebGL 首屏
- 04确保无 JavaScript 时页面内容仍可见
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ paragonhq/ stealth-skills --skill scroll-and-webgl安装后在 Claude Code 中输入/scroll-and-webgl确认已加载。
详细介绍
滚动与 WebGL
这是唯一一个允许以动态效果为核心的层级。规则不是更宽松,而是更严格,因为失败的结果可能是:页面在作者的笔记本电脑上很漂亮,在其他地方却无法使用。
适用场景
| 场景 | 是否允许 |
|---|---|
| 营销页面、发布页面、作品集、案例研究 | 可以,但须遵守下方的预算 |
| 文档、博客、阅读内容 | 最多使用标题揭示效果 |
| 产品 UI、仪表板、工具、设置 | 不允许。请改用 motion 技能 |
如果有人提议在产品界面上添加滚动效果,答案就是不行,原因是人们会数百次使用产品界面。
预算
开始前设定,结束时检查:
- 在 CPU 降速 4 倍的情况下达到 60fps,覆盖整个滚动过程。每秒掉一帧就算不合格。
- LCP 低于 2.5s,且效果已启用。主视觉区域的文字必须位于 HTML 中,并在任何脚本运行前可见。
- INP 低于 200ms:滚动处理程序绝不读取布局;所有操作都在合成器上进行,或批量放入
requestAnimationFrame。 - 内存:每个页面只能有一个 WebGL context。纹理尺寸低于 2048px。组件卸载时释放资源。
- 每个视口只用一种视觉效果。 主视觉区域着色器 或 固定滚动场景 或 拆分文本揭示效果,同一时间只能使用一种。两种同时出现就会显得杂乱。
滚动驱动效果,按成本从低到高排列
1. CSS 滚动驱动动画
无需 JavaScript,在合成器上运行,只需一条媒体查询即可遵循减少动态效果偏好:
.reveal {
animation: fade-up linear both;
animation-timeline: view();
animation-range: entry 0% entry 40%;
}
@keyframes fade-up { from { opacity: 0; transform: translateY(24px); } }
适用于:进入视口时揭示、进度条、幅度低于 10% 的视差、收缩式粘性页眉。不支持的浏览器会回退到“直接显示”,这是正确的回退方式。
2. IntersectionObserver + 类名
适用于 CSS 时间线不受支持,或希望使用库的入场效果时的一次性揭示效果。观察元素、添加类名、取消观察。阈值为 0.15–0.3;设置 rootMargin: "0px 0px -10% 0px",让元素在到达底边前稍早揭示。
3. GSAP ScrollTrigger
适用于任何需要滚动同步、固定或按顺序播放的效果:
- 使用
scrub: true(或0.5–1,以增加少量延迟)将时间线与滚动条关联。没有其他方式能达到同样流畅的效果。 - 使用
pin: true,让场景播放期间固定某个区段;固定区段的滚动距离应低于 300vh,否则用户容易迷失。 - 在固定元素上设置
anticipatePin: 1,避免出现一帧的跳动。 - 每个场景使用一个
ScrollTrigger,而不是每个元素一个;在时间线内排列元素。 - 使用
matchMedia,在 768px 以下以及减少动态效果偏好开启时移除固定和滚动同步效果。 - 字体加载完成后,以及布局发生任何变化后,调用
ScrollTrigger.refresh()。
4. Lenis(平滑滚动)
Lenis 通过插值处理滚轮输入,让滚动关联动画感觉连贯。它适用于营销页面,不适用于其他场景:
- 减少动态效果偏好开启时禁用。在触屏设备上禁用(原生惯性滚动效果更好)。在任何拥有独立滚动条的区域内禁用(设置
data-lenis-prevent)。 - 持续时间设为 1.1–1.3,
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t))。 - 让 Lenis 驱动 GSAP 的 ticker(
lenis.on("scroll", ScrollTrigger.update)),确保两者同步。 - 锚点应通过
lenis.scrollTo(el, { offset: -header })滚动,而不是使用原生scrollIntoView。 - 模态框和菜单打开时调用
lenis.stop()。
文本揭示效果
按行或按词拆分文本,绝不按字符拆分,除非文本很短(例如单词徽标)。字符逐个显示会产生打字机效果,很快就会显得过时。
- 按行:将
translateY(100%)放在overflow: hidden包装元素内,错开 80ms,使用 700ms 的 ease-out-expo。这种经典效果至今仍是最佳选择。 - 按词:透明度 +
translateY(6px)+blur(6px→0),错开 30ms。 - 只播放一次。首次进入视口时播放。之后,文本就只是文本。
- 父元素的
aria-label保存完整字符串;拆分后的 span 设置aria-hidden。没有 JS 时,文本应以普通形式显示。 - 窗口尺寸改变时重新拆分;在一种宽度下拆分的文本换到另一种宽度时,换行会出错。GSAP SplitText 会处理此问题;手动拆分则必须监听窗口尺寸变化。
Canvas 和 WebGL
适用时机
着色器背景、能响应指针移动的 3D 对象、粒子场、产品模型。如果用 CSS 渐变和噪声 SVG 就能实现,那就用它们。
如何做
- 设备像素比最高为 2,大型画布设为 1.5。Retina 3× 会让像素数变成 4 倍,却没有可见提升。
- 离屏时暂停:使用 IntersectionObserver,并在
document.hidden时暂停。隐藏的画布仍在消耗 GPU,这是笔记本发烫最常见的原因。 - 使用
ResizeObserver调整尺寸,并进行防抖;只重置一次绘图缓冲区,而不是每帧都重置。 - 页面只保留一个动画循环。多个
requestAnimationFrame循环会互相争用。 - 延迟导入库和场景。先渲染主视觉区的文本;准备就绪后,画布在 400ms 时淡入。主视觉区空白地等着 three.js 加载,是不合格的。
- 卸载时释放几何体、材质、纹理和渲染器。在 SPA 中,路由切换时发生内存泄漏很常见。
- 指针响应应经过缓动:每帧将目标值以 0.08–0.12 的比例插值过去。直接进行 1:1 跟踪会显得很机械。
- 备用方案:使用颜色和视觉分量相近的静态图像或 CSS 渐变,在 WebGL 不可用、启用减少动态效果,或设备性能较低时启用(可将
navigator.hardwareConcurrency < 4和deviceMemory < 4作为粗略判断条件)。
将着色器作为材质
用于背景的片元着色器比 3D 场景更省资源,而且往往效果更好:缓慢变化的渐变噪声、抖动光晕、颗粒效果。代码控制在 30 行以内,用时间和指针 uniform 驱动动画,并降低对比度,确保上方的文本清晰可读。抖动(有序抖动或蓝噪声)可以隐藏深色渐变中的色带,并增加质感。
3D
使用 Three.js 或 React Three Fiber。模型使用压缩的 glTF(Draco 或 Meshopt),大小控制在 2MB 以内,并使用与最终轮廓相符的 <Suspense> 回退内容。环境光使用小型 HDRI。除非阴影是效果重点,否则关闭阴影。后期处理最多使用一个通道,也可以完全不用。
加载策略
- HTML 包含所有文本,并为首屏图片指定尺寸。首次绘制时即可见。
- 加载字体(两个字重,预加载)。
- 布局稳定。此时记录 LCP。
- 加载滚动库和观察器。
- 画布、WebGL、模型:延迟加载,在浏览器空闲后淡入。
即使第 5 步失败或被跳过,页面也应完整可用。这就是检验标准。
减少动态效果与不启用 JS
在 prefers-reduced-motion: reduce 时:
- 不使用 Lenis。不使用固定定位。不使用逐帧同步动画。不启用画布动画(静态画面可以)。
- 不运行显现动画;内容保持可见。
- 视频不自动播放。
不启用 JavaScript 时:
- 显现动画隐藏的所有内容都应可见(
html:not(.js) [data-reveal] { opacity: 1 })。 - 页面从上到下正常阅读,场景预留的位置不应出现空白。
页面会被关掉的迹象
| 迹象 | 修复方法 |
|---|---|
| 滚动被劫持;滚轮手感不对 | 仅在营销页面使用 Lenis;触屏设备上关闭,或完全不用 |
| 固定定位的区块超过 300vh | 精简场景;缩短固定定位时长 |
| 文本只有在脚本拆分后才存在 | 将文本放在 HTML 中;拆分只是增强效果 |
| 画布离屏后仍在运行 | 使用 IntersectionObserver 暂停 |
| 全屏画布的 DPR 为 3 | 限制为 2 |
| three.js 加载完成前主视觉区一片空白 | 先显示文本;画布淡入 |
| 视差效果达到 30% | 控制在 10% 以下,或完全不用 |
| 段落逐字显现 | 改为逐行显现 |
| 仪表板上使用特效 | 删除特效 |
目录中的资源
- GSAP 和 ScrollTrigger、SplitText——这些配方所需的时间轴工具。
- Lenis——平滑滚动,但请注意上述限制。
- Three.js、React Three Fiber——用于网页 3D。
- Codrops——关于滚动、WebGL 和转场效果的实验性教程。
- Maxime Heckel——由实际开发并发布这些效果的人讲解着色器和 WebGL。
- Godly、Awwwards——复制效果前,先研究它的用途。
- 更多资源见 stealth.pm/build。