关于 taste-gsap
taste-gsap 是面向编码 Agent 的 GSAP 动画开发技能,涵盖无障碍动效、滚动效果、性能与清理规范。
主要功能
- 01用 gsap.context 限定动画范围
- 02通过 matchMedia 控制动效
- 03限制并组织固定滚动区段
- 04优先使用变换和透明度动画
- 05提供静态与减少动效回退
适用场景
- 01规划 GSAP 滚动触发区段
- 02让动画遵循减少动效偏好
- 03检查固定与滚动联动效果
- 04保持动画布局性能稳定
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ madebymustafa/ design-taste --skill taste-gsap安装后在 Claude Code 中输入/taste-gsap确认已加载。
详细介绍
taste-gsap
GSAP 是手术刀,不是亮片枪。一个干净的固定区段胜过九层视差效果,也更经得起代码审查。
1. 架构(不可妥协的基础)
- 将每个页面的所有动画都封装在单个
gsap.context(() => { ... }, rootElement)中——它会限定选择器的作用范围,并支持即时清理。 - 所有内容都要通过
gsap.matchMedia()控制:(prefers-reduced-motion: no-preference)——所有动画都放在这里。减少动态效果的用户会看到完整的静态页面。(min-width: 768px)——复杂效果(固定、滚动控制、逐字符拆分)仅限桌面端;小屏幕使用淡入效果或不添加动画。
matchMedia回调会返回清理函数;在 SPA 中,卸载时调用ctx.revert()。- 每个区段使用一个时间轴,并以区段命名;不要创建包含 40 个补间动画的巨型全局时间轴。
- Lenis(免费版,不是 ScrollSmoother):
new Lenis({ duration: ~1.1, anchors: true }),通过lenis.on("scroll", ScrollTrigger.update)+gsap.ticker.add(t => lenis.raf(t * 1000))+gsap.ticker.lagSmoothing(0)将其接入 ScrollTrigger。预加载期间调用lenis.stop(),完成后调用lenis.start()。减少动态效果时绝不使用平滑滚动。
2. 固定与滚动控制规范(最容易出大问题的地方)
- 每个页面最多固定两个区段。 每次固定都会占用滚动线程。两个是上限;通常一个就够。
- 固定设置:
pin: true, pinSpacing: true(默认值,保留它)、start: "top top"、end: "+=200%"到"+=300%"。结束距离越长,滚动控制越慢,在移动设备上也越容易卡顿。 - 横向画廊(Awwwards 的标志性做法):固定区段,平移轨道(仅使用
x,绝不使用left);设置end: () => "+=" + (track.scrollWidth - innerWidth),以便在调整窗口大小时重新计算距离;使用scrub: 1,仅限桌面端。必须保留原生overflow-x+ scroll-snap,作为无 JS 或减少动态效果时的回退方案。 - 拖动轮播(Magma-school):
Draggable.create(track, { type: "x", inertia: true, bounds: () => ({ minX: -(track.scrollWidth - wrap.offsetWidth), maxX: 0 }) })——将边界设置为函数,以便在调整窗口大小时重新计算。edgeResistance约为 0.9;保留原生overflow-x+ snap 作为无 JS 回退方案,并添加光标或 UI 提示,说明条带可以拖动。 - 复杂区段使用
scrub: 0.6–1;scrub: true仅用于简单补间动画。没有平滑处理的滚动控制补间动画会与滚动产生冲突。 - 绝不在另一个固定区段内固定;第一个区段的占位空间结束前,绝不开始第二个固定区段。字体或图片加载后调用
ScrollTrigger.refresh()(window.load+document.fonts.ready),否则所有内容都会错位。 - 发布前,在 768px 宽度下测试固定效果——让移动端用户被困在固定区段中是 bug,不是功能(因此固定效果仅在
(min-width: 768px)条件下启用)。 - 对依赖刷新结果的触发器设置
invalidateOnRefresh: true,避免重新布局后位置逐渐偏移。
3. 动画内容(仅使用变换和不透明度)
- 仅对
x、y、scale、rotation、opacity添加动画——绝不对top/left/width/height/margin/filter添加动画(布局抖动会导致卡顿)。 - 文本揭示:默认按行拆分(使用
overflow: hidden包裹,并将yPercent: 110移至 0)。仅对主视觉标题按字符拆分,且只能在桌面端条件下使用。 - 不要滥用
will-change——动画期间将其设置在动画元素的包装元素上,动画结束后移除(或交由 GSAP 管理)。每个will-change都是在承诺占用内存。 - 保持真实的 60fps:不要在一帧内对 200 个元素应用交错动画,不要滚动控制 40 个 DOM 属性。使用
gsap.utils.toArray批量处理并设置交错效果——一个补间动画,多个目标,开销低。
4. 揭示动画模式(专业人士的默认做法)
- 起始状态应写在代码中,而不是 CSS 中: 元素在 CSS 中默认可见;在 matchMedia 条件门控内使用
gsap.from(...)和immediateRender: true,仅当 JS 和动态效果均启用时才隐藏元素。如果 GSAP 未能加载,页面仍然完整——这是安全保障。 - 标准揭示动画:
opacity: 0, y: 40, duration: 0.9, ease: "power3.out",触发位置为top 82%,内容区块使用once: true。 - 主视觉开场:加载时播放
gsap.timeline()——元素之间错开 80–120ms,使用ease: "expo.out"或power4.out,总时长少于 1.4s。页面是一个有开篇的故事,而不是幻灯片。 - 数字递增动画:对代理对象执行补间(
gsap.to(obj, { v: target })),在onUpdate中写入el.textContent,snap为可选项。触发器上使用once: true。
5. 一整套禁忌(每项都不可接受)
- 在没有平滑滚动层(Lenis)的情况下劫持滚轮滚动,之后又不进行测试——原生滚动是一项特性。
- 内容尚未存在就触发动画(缺少
ScrollTrigger.refresh())。 - 无限滚动字幕在减少动态效果偏好下仍未暂停——也要通过 CSS 中的
@media (prefers-reduced-motion: reduce)停用,而不只是用 JS。 - 任何自动播放内容:主视觉视频、手风琴,以及无需输入就会移动的轮播。Awwwards 式的自信,体现在片刻之间的静止。
- 页面若要靠动画才能“惊艳”:如果静态截图很乏味,动画只是粉饰,而不是设计。
6. 检查清单(出现以下情况时,动画不予通过……)
- 没有使用
gsap.context+matchMedia进行条件门控(偏好减少动态效果的用户仍会看到动画)。 - 固定区块超过两个,或在 768px 以下使用固定区块。
- 使用了任何动画布局属性(
top/left/width/height)。 - 字体或图像加载后没有调用
ScrollTrigger.refresh()。 - 在桌面端条件门控之外拆分逐字符动画。
- 滚动字幕或自动播放没有减少动态效果的停用开关。
- 静止画面(无 JS)不是完整、精美的页面。
- 动画只是为了动画而存在——每个动作都必须服务于故事,否则就删掉。
评论
支持 Markdown