Communitygithub.com

igal-commits/animating-web-pages

用于添加或修复网页动效——滚动揭示、首屏入场、淡入淡出、环境循环、循环背景视频、图片轮播、粘性元素——或处理动画卡顿、时序错误、屏幕外播放或完全不运行等问题。

animating-web-pages 是什么?

该技能是一套系统化排查与修复网页动效问题的指南。它首先强调,在调整缓动曲线之前,必须先证明动画确实在你认为的元素上运行。技能覆盖了动画完全不运行的七大常见原因(如选择器匹配零个元素、透明度未设置、触发时机落后于布局等),并给出精确的检查步骤和代码示例。对于入场动效、环境微动效、滚动揭示、背景视频、卡顿、粘性定位等具体场景,提供了严格的设计规则和反模式。此外,还详细说明了无障碍动效(缩减动效)、无脚本降级和移动端适配的完整回退方案,以及一个可操作的验证清单。该技能适用于任何基于 CSS/GSAP/ScrollTrigger 的网页动画开发与调试场景。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/igal-commits/landing-page-skills/tree/HEAD/skills/animating-web-pages

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

网页动效制作指南

概述

网页动效的失败通常有两种形式:它不运行,或者运行得很差。两者都有具体且可查找的原因。猜测缓动曲线是最后才做的事,而不是第一步。

核心原则:在调整动画之前,先证明它正在你期望的元素上运行。

有关固定和擦除场景的设计,请参见 choreographing-scroll-stories。此技能关注的是机制:让动效运行并运行良好。

动画完全不运行时

按此顺序检查。前三种情况几乎涵盖了所有问题。

症状原因检查方法
什么都不动,元素可见选择器匹配零个元素在控制台运行 document.querySelectorAll(sel).length
什么都不动,元素不可见揭示元素设置了 opacity: 0 但触发从未发生查看元素的计算 opacity 值
淡入淡出从未发生两个图层都停留在 opacity: 1;只有“开”状态规则存在必须有一个定义引入层为 0 的初始状态规则
加载时立即触发触发器在布局稳定前就执行在 load 事件后运行,而非 DOMContentLoaded,当图片会影响页面大小时尤需注意
触发一次后不再触发once: true 且元素重新进入视口这是否符合预期?
在隐藏标签页或无头面板中不运行当 document.hidden 时时钟暂停强制运行:gsap.ticker.lagSmoothing(0); gsap.ticker.tick()
触发器偏移几百像素图片在触发器测量之后加载在加载和调整大小时调用 ScrollTrigger.refresh()(200 毫秒防抖)

淡入淡出的情况值得详细说明,因为它看起来在代码审查中是正确的:

/* 仅此一项无效。 */
.tile.is-swapped .layer.is-alt { opacity: 1 }

/* 初始状态才是使其成为动画的关键。 */
.layer.is-alt { opacity: 0 }
.tile.is-swapped .layer.is-alt { opacity: 1 }

首屏入场与环境微动效

  • 标题应快速可见。 将入场动画裁剪至标题在大约 1.1 秒内显示。一个 3.8 秒的入场动画就是加载屏幕。
  • 图片通过淡出某个单元格并淡入另一个单元格来切换。它们从不滑动。 每张照片是一个独立元素,绝不会在屏幕上出现两次,也绝不会在副本淡入的同时淡出。
  • 环境变化应缓慢且不规则。 每 1.8 到 3.4 秒更换一个图块,这会让网格看起来是活的,而单个图块变化缓慢。不要为了加快每个图块的变化速度而缩短间隔。
  • 按轮次随机排列。 每个图块在重复之前都会变化一次,且每次选择都来自最近三次中较远的一半。
  • 暂停无人可见的内容。 环境循环会在屏幕外和隐藏标签页中停止,并在它们原来的位置恢复。
  • 入场动画不会在每次返回时重播。 入场动画稳定后,除了环境层之外,其他都不再动。

滚动揭示

  • 仅对加载时实际位于首屏下方的元素设置 opacity: 0。全部隐藏意味着任何首屏元素都不可见,直到可能永远不会发生的滚动发生。
  • 在 top 88% 处揭示,而非完全交叉。等待元素完全进入视口意味着当读者已经看到它时,它才开始动画。
  • 兄弟元素之间交错延迟,组件内部不延迟。卡片之间 60-90 毫秒,卡片内部为 0。
  • 使用每组的单个观察者进行批处理,而不是每个元素对应一个观察者。

视频

自动播放的背景视频有一个普遍问题:视频在屏幕外播放,观察者会暂停它,而当读者到达时,循环已经进行了 15 秒,开场节拍已经丢失。

完全移除 autoplay 属性。
在初始化时调用 pause() 和 load()。
仅在首次交叉时:currentTime = 0,然后 play()。

在低于 992px 宽度或低于 1.5 设备像素比时,提供 1x 文件,并在视频解码前提供海报帧。主页的重度视频也是带宽消耗大户:要审计它。

卡顿

静止照片上的缓慢漂移是最常见的原因。每帧移动小于约 1 像素的 Ken Burns 效果会在每帧重新采样图像,看起来是闪烁而非运动。

  • 要么移动足够多成为运动,要么根本不移动。
  • 对于渲染视频中的静态帧,直接逐字节复制编码帧,而不是重新编码完全相同的帧。否则会产生漂移。
  • 检查循环接缝。如果第 0 帧和最后一帧不同,循环会在每个周期明显跳跃。测量它们之间的平均绝对差值。
  • 不要双重平滑。一个平滑滚动库加上一个较大的 scrub 值会导致页面滞后于滚轮。使用平滑滚动库时,请使用 scrub: 0.2。

粘性定位

CSS 网格内的 position: sticky 是相对于网格区域解析,而不是整个容器。一个只与其内容同高的粘性项目没有可移动空间,会立即释放。

.grid  { align-items: stretch }      /* 区域现在为全高 */
.col   { position: static; height: 100% }
.inner { position: sticky; top: 10rem }

回退方案

每个动画都要有三个回退方案,写在动效规范中并经过测试:

  • 缩减动效。 prefers-reduced-motion: reduce 和一个 ?static 查询会渲染堆叠的、完成的状态:没有任何固定、没有任何隐藏、每个场景都处于其结束状态。也要覆盖你添加的每个 CSS transition。列出动画选择器但忘记对容器设置过渡,会导致颜色仍然在滑动。
  • 无脚本。 所有内容保持可见。如果脚本从未启动,大约 3.5 秒后会添加一个类,CSS 会揭示其下的所有内容。
  • 手机端。 固定场景会堆叠。说明这一点并进行测试。

验证

不要仅凭代码报告动画正常工作。在页面中确认:

  • 每个动画组的选择器计数非零
  • 在任何滚动之前,首屏元素的计算 opacity 为 1
  • 视频 currentTime 在首次可见时低于 0.5 秒
  • 缩减动效模拟使页面对读者完全可读且无动效
  • 无脚本状态下所有内容保持可见
  • 不使用 width、height、top 或 left 动画;使用 transform 和 opacity
  • 环境循环在屏幕外时不进行任何样式更改

警示信号

  • 在确认选择器匹配之前调整时长或缓动
  • 没有对应初始状态规则的 .is-active 规则
  • transition: all
  • 因为感觉“段落空洞”而添加的动画
  • 图片滑动
  • 同一张图片在屏幕上出现两次

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

相关技能