把细微的互动,收藏成卡片。项目包含从现有首页抽离的九个动画,以及新增的 DeepSeek「潜入答案」,放进可以暂停、重播和筛选的卡片中。查看效果时,也能直接打开对应源码。
只有动画,没有商品、价格、购买流程、登录或后台接口。
需要 Node.js 22.18+(22 系列)或 24+,推荐 Node.js 24 LTS,与 .nvmrc 和 CI 一致。
git clone https://github.com/7Mrlan/animation.git
cd animation
npm ci
npm run dev打开终端显示的本地地址,通常是 http://localhost:5173。
npm run typecheck # TypeScript 严格检查
npm test # 时间轴、几何、视口窗口与观察器注册表测试
npm run build # 类型检查 + 生成 dist/
npm run preview # 本地预览生产构建
npm run check # 类型 + 测试 + 构建,与 CI 一致
npm run format # 格式化代码和文档- 沿用 React + TypeScript 和原动画组件的实现,减少重写带来的偏差,展示与运动逻辑分别维护。
- 使用 Vite 构建静态演示,无需服务端渲染、路由或后端,产物可直接托管。
- 样式采用 普通 CSS,可以直接阅读关键帧、变量和伪元素,不依赖样式框架。
- 每个动画场景通过
React.lazy单独分块,卡片进入预加载窗口时才请求 JS 与对应 CSS;Duo 再按需加载 Lottie light SVG,其余动画使用 CSS 或requestAnimationFrame。 - 运行时只有 React、React DOM 和 Lottie 三个依赖;字体使用系统字体,动画素材本地提供。
| 卡片 | 内容 | 核心入口 |
|---|---|---|
| 01 层叠之间 | 卡片抽出、下沉、归位;悬停/聚焦/触摸展开 | CardDeck.tsx · 时间轴 |
| 02 灵感引力场 | Claude 标志、纸片、轨道、指针光晕 | ClaudeField.tsx |
| 03 星光慢行 | Gemini 星形、双轨旋转、星点呼吸 | Membership.tsx |
| 04 代码微光 | 终端浮动、指针准星、背景网格视差 | CodeWorkspace.tsx |
| 05 一声 Hello | Duo Lottie 循环与静态海报回退 | DuoPlayer.tsx |
| 06 讯息涟漪 | 手机浮动、消息交错出现、信号呼吸 | TelegramSignal.tsx |
| 07 光标来访 | 浮动窗口、光晕与装饰光标跟随 | ProfileWindow.tsx |
| 08 小小宇宙 | 轨道、悬浮粒子、中心呼吸 | OrbitCore.tsx |
| 09 一封轻盈的信 | 伪元素信封与反向浮动标签 | FloatingMail.tsx |
| 10 潜入答案 | 拖拽小鲸、流动水面、曲线巡游与鲸跃 | DeepSeekDive.tsx · 运动模型 |
每个组件旁边都有对应 CSS。Gemini 与 Duo 共用 Membership.css。查看文件面板还会显示动画所需的时间轴、播放器或 Hook,便于一起复制。
- 先看
FloatingMail/TelegramSignal,理解@keyframes、伪元素和错峰延迟。 - 看
GeminiOrbit/OrbitCore,理解多组动画如何叠加,注意每组transform的作用层级。 - 看
ClaudeField/CodeWorkspace/ProfileWindow,理解指针坐标、CSS 变量和 RAF 合帧。 - 看
CardDeck和subscription-motion.ts,理解离散阶段、连续插值和层级交接。 - 看
DuoPlayer,理解异步加载、取消、失败回退和动画实例销毁。 - 看
DeepSeekDive,理解拖拽、水面冲击、曲线巡游和抛物线鲸跃。
完整的 动画学习手册 包含十个动画的逐章讲解,以及基础、播放控制、术语和 AI coding 四个专题。每个动画章节都有参数说明、动手练习和可复制的提示词。
- 从基础开始
- 按卡片查找章节
- 动画术语与需求翻译
- 动画生成与产品创意提示词,含十份动画规格和四份新产品创意模板,可直接交给没有项目上下文的 AI
src/
animations/ 独立动画组件与对应 CSS(按场景分块加载)
lib/ 动画控制器、运动/水面计算、视口窗口与观察器注册表
hooks/ 可见性窗口、滚动显现与系统减少动态效果偏好
components/ 卡片外壳、源码对话框、图标
catalog.tsx 十个动画的名称、说明、懒加载场景与源码路径
App.tsx 筛选与全局播放状态
styles.css 新的卡片画廊外观、滚动显现与窗口占位
public/brands/ 原动画所需的本地素材和第三方许可
- 只有视口附近(上下各 600px)的卡片会挂载动画场景,场景的 JS 和 CSS 也在这时才开始加载。离开 1000px 释放边界后卸载,卡片外壳始终保留,因此网格高度、锚点、查找和键盘顺序都不受影响。这是“动画场景的虚拟滚动”,不是把正文也虚拟化。
- 预加载与释放使用不同的边界,避免停在边界时反复挂载;所有卡片同时只保留 4 个 IntersectionObserver(预加载、释放、显现、播放),页面可见性和减少动态效果也使用共享订阅。观察器的共享、多订阅和清理有独立单元测试。
React.lazy的场景分块用Suspense占位;单个分块加载失败由卡片级错误边界兜底,不会让整页白屏。进入视口附近时开始加载,因此不会为了尚未出现的卡片下载全部动画代码。- 卡片滚动进入视口时淡入上移;桌面端下方区块淡入缩放,触屏使用更轻的透明度显现。显现只发生一次,向上滚回不会重新隐藏;键盘聚焦或指针进入会提前显现。显现观察整张卡片,播放观察内部舞台,文字可见但舞台在视口外时不会跑动画。
- 预加载窗口内离屏、页面隐藏、打开源码面板时暂停,返回后继续;越过 1000px 释放边界后场景卸载,回到附近会重建并从初始状态开始。
- 每张卡片和整页均可手动暂停,重播会重建该动画实例。
- 遵循系统
prefers-reduced-motion,展示静态构图、Duo 海报,不开启循环动画和显现过渡;卡片展开、拖鲸等直接交互仍然可用。 - 源码面板使用原生
<dialog>,支持 Esc、焦点约束及关闭后焦点返回。 - 触屏设备使用更轻的绘制路径:关闭
backdrop-filter、持续的drop-shadow/blur和卡片的scale显现,改用不透明控件与 40px 触摸目标;指针场景在pointerdown就响应,触摸抬手不会把位置重置。 - Duo 的 Lottie runtime 在首屏空闲预热;Lottie 实例在滚动停止 180ms 后才创建,触屏滚动期间暂停播放,避免 SVG 构建和播放落在滚动帧里。DeepSeek 的拖拽手柄保留
touch-action: none。 - 手机可点击层叠卡片展开;所有卡片均提供触摸可用的播放工具。
npm run build 后部署 dist/。base: './' 让产物支持根目录及 /animation/ 等子目录;Lottie 和海报路径也遵循同一规则。项目不需要环境变量。
线上演示:轻动 · Animation Studies。
仓库使用 .github/workflows/check.yml 自动检查和发布到 GitHub Pages。每次推送到 main,先安装依赖并运行 npm run check(类型检查、测试和构建),成功后上传 dist/ 并部署。PR 和其他分支只检查,不发布。也可在 GitHub Actions 的 Check and deploy Pages 页面手动运行工作流。
首次启用时,需要在仓库 Settings → Pages → Build and deployment 中选择 GitHub Actions;后续发布由工作流自动完成。本项目不需要配置部署密钥或环境变量。
动画来自原项目当前工作区,只复制到新项目,原项目保持不动。保留原来的动画形状、配色、关键帧和主要时序,移除业务依赖,并重新设计展示卡片。信封示例根据原首页保留的 CSS 恢复最小 DOM。品牌图形与 Duo 动画保留其原有权利说明;详见 ATTRIBUTION。