Communitygithub.com

Niltra/web-scrollytelling

实现苹果风格的电影级滚动体验(固定主角、Canvas帧序列、视频擦洗、章节叙述),基于GSAP ScrollTrigger,无滚动劫持,并测量性能。适用于构建动画主角、固定区域、横向画廊或任何“让网页像可操控的视频”的效果。

web-scrollytelling 是什么?

这是一个面向开发者的专业AI Agent技能,用于构建电影级滚动讲述(scrollytelling)体验,灵感来源于Apple官网的叙事方式。它基于GSAP ScrollTrigger,严格遵循“单一固定容器、单一时间线”的架构,确保动画与原生滚动同步,绝不劫持用户滚动。技能核心包括帧序列动画(Canvas或img切换)、视频擦洗(scrubbing)、章节覆盖层以及横向画廊等高级效果,并内置了严谨的性能优化指南(如帧数限制、窗口化加载、devicePixelRatio限制)。该技能支持所有主流浏览器和框架(如React、Vue、普通HTML),通过详细的SKILL.md文档提供了从架构决策到具体实现的完整规范,确保开发者能构建出流畅、可访问且SEO友好的滚动叙事页面。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Niltra/agent-skills-web/tree/HEAD/skills/web-scrollytelling

在你喜欢的 AI 中提问

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

文档

电影级滚动讲述(Scrollytelling)

基础架构(不可妥协)

  • 单一固定容器,单一时间线。 主角(或核心章节)固定一个容器,其持续 N 个视口高度,进度 0→1 驱动一切。如果有多个滚动动画在竞争,那只是一个普通的有特效的网页,不是本技能的效果。
  • 绝不滚动劫持。 用户通过原生滚动控制速度;动画被“擦洗”(scrub: true)。禁止劫持鼠标滚轮或替用户移动滚动条。
  • 单一动画循环。 Lenis 集成到 GSAP 的 ticker 中(autoRaf: false),ScrollTrigger 同步;或者使用原生滚动配合 scrub。两个循环竞争是卡顿的头号原因,且无法通过优化资源解决。
  • 文本存在于 DOM 中,叠加在 Canvas 之上,根据进度分层显示。绝不在 Canvas 内部渲染文字。

帧序列(Apple 式技术)

  • 首选来源:真实视频 → 使用 ffmpeg 导出帧(fps=12..24,按屏幕分辨率缩放)。导出 WebP 格式,质量参数 q≈68-75,根据实际效果微调,而非凭直觉。
  • 最多约 90-120 帧;在移动端提供一半帧数或 720p 版本。
  • 使用 滑动窗口 加载:仅保留当前帧周围 ±6-10 帧;绝不解码完整序列(完整 1080p 序列会杀死 iOS Safari)。
  • 预加载使用 IntersectionObserver(rootMargin ~800px),requestIdleCallback 进行解码,fetchPriority="low"(主角帧除外)。
  • Canvas 的 devicePixelRatio 限制为 1.5,计算 cover/contain 模式;仅绘制已解码的帧;如果缺少精确帧,使用最近已加载的帧(绝不绘制黑色)。
  • 使用不可变缓存(Cache-Control: public, max-age=31536000, immutable),文件名包含版本号。
  • 降级方案:当检测到 prefers-reduced-motion 或网络连接慢时,使用静态海报 + 无固定效果的普通章节。
  • 更简单的替代方案(当不需要后期处理时):通过更换 <img> 的 src 来切换帧(在所有浏览器中可靠,无需 Canvas)。

视频擦洗(若使用 <video>)

  • 在单一 requestAnimationFrame 中由进度驱动 currentTime,并做平滑处理;合并 seeks(Safari 会累积队列)。
  • 将视频作为 blob 加载以确保 100% 可 seek;使用短 GOP 编码(关键帧间隔短),否则擦洗会卡顿。
  • 绝不用 autoplay:由滚动驱动播放。

章节与覆盖层

  • 关键节拍由进度范围定义(10%、35%、60%、85%……),包含淡入淡出效果,并交替从不同侧进入以创造节奏感。
  • 每个章节在 DOM 中按正确的语义顺序保留其文本,以利于 SEO。
  • 横向画廊:固定该区域 + 将 x 绑定到进度;卡片向中心缩放。

性能与验证

  • 仅动画化 transform、opacity 和 filter 属性;will-change 按需使用并在动画结束后移除。
  • 使用 requestAnimationFrame 在本地和真实移动设备上测量帧率(如可能);目标是在中等配置的笔记本电脑上达到 60fps。
  • Headless 截图可能读取到黑色 Canvas(缓冲区未保留):需通过 DOM 验证(计算样式、元素、暴露在 window.debug 中的状态),并测试弱网模式。
  • 在 375/768/1280/1440 宽度下测试;桌面浏览器的窗口缩放无法复现 iOS 的内存崩溃:需在 QA 报告中明确说明。

推荐的数据结构

一个有序的“场景”数组:每个场景为 { type: 'frames' | 'overlay' | 'galeria', ... }。要重新编排叙事顺序,只需重新排列该数组。帧通过模式引用(例如 /seq/plano-01/0001.webp)。

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

相关技能