关于 scrollytelling
面向网页创作者的设计技能,用于规划滚动叙事体验,涵盖叙事模式、实现方式、无障碍设计和性能建议。
主要功能
- 01五种滚动触发视觉手法
- 02四类页面布局模式
- 03CSS、IntersectionObserver、GSAP 和 Motion 实现建议
- 04减少动态效果与无障碍指导
- 05性能和移动端建议
适用场景
- 01规划多媒体长篇报道
- 02为产品叙事选择布局
- 03为网页添加无障碍滚动效果
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ doodledood/ claude-code-plugins --skill scrollytelling安装后在 Claude Code 中输入/scrollytelling确认已加载。
详细介绍
滚动叙事技能
构建由滚动驱动的叙事体验,在用户滚动时逐步揭示内容、触发动画,并营造沉浸式叙事。
什么是滚动叙事?
定义:“一种叙事形式,读者在滚动浏览在线文章时,视觉和文本元素会随之出现或变化。”当读者滚动时,发生的事情不同于常规的文档移动。
起源:《纽约时报》的《雪崩:隧道溪的雪崩》(2012 年),该作品荣获 2013 年普利策特稿写作奖。
为何有效:滚动叙事利用了一个基本的心理学原理——人类渴望掌控感。每一次滚动都是一次微小的承诺,从而提升参与度。用户掌控节奏,比被动消费建立更深层的连接。
实测影响:
- 页面停留时间比静态内容长 400%
- 信息回忆率提升 67%
- 社交分享率提高 5 倍
- 转化完成率提升 25-40%
核心原则
1. 故事优先,技术其次
最大的错误是以技术而非叙事为先导。滚动叙事应当增强故事,而非展示特效。
2. 用户自主权与渐进式披露
用户掌控节奏。信息逐步揭示以保持好奇心。这将预定节奏转变为用户掌控的叙事流。
3. 顺序结构
与层级化的网页内容不同,滚动叙事要求线性推进,并具有清晰的叙事节拍。每个部分都建立在前一部分之上。
4. 有意义的变化
每一个由滚动触发的效果都必须服务于叙事。无谓的动画只会分散注意力,而非增强效果。
5. 克制胜于炫技
并非每个部分都需要动画。微妙的过渡往往比持续的特效更有效。这种形式应当放大内容的信息,而非与之对抗。
5 种标准技法
一项分析 50 篇滚动叙事文章的研究识别出以下核心模式:
| 技法 | 描述 | 最适合 |
|---|---|---|
| 图形序列 | 在滚动阈值处完全变化的离散视觉元素 | 数据可视化、分步说明 |
| 动画过渡 | 状态之间的平滑变形 | 状态变化、随时间演变 |
| 平移与缩放 | 滚动控制视觉元素中可见的部分 | 地图、大图、空间叙事 |
| 电影滚动 | 逐帧推进,营造类似视频的效果 | 产品展示、3D 物体揭示 |
| 展示与播放 | 交互元素在滚动路标处激活 | 多媒体、音频/视频集成 |
布局模式
模式 1:并排粘性(最常见)
经典的滚动叙事模式:图形“粘住”不动,而叙事文本在旁滚动。当叙事结束时,图形“解除粘住”。
┌─────────────────────────────────────┐
│ ┌──────────┐ ┌─────────────────┐ │
│ │ Text │ │ │ │
│ │ Step 1 │ │ STICKY │ │
│ ├──────────┤ │ GRAPHIC │ │
│ │ Text │ │ │ │
│ │ Step 2 │ │ (updates with │ │
│ ├──────────┤ │ active step) │ │
│ │ Text │ │ │ │
│ │ Step 3 │ │ │ │
│ └──────────┘ └─────────────────┘ │
└─────────────────────────────────────┘
何时使用:数据可视化故事、分步说明、需要持续视觉背景的教育内容。
实现:使用 CSS position: sticky(而非 JavaScript 滚动监听器),以获得更好的性能和优雅降级。
模式 2:全宽分节
内容横跨整个视口,并具有基于分节的过渡。
何时使用:高度视觉化的叙事、沉浸式品牌叙事、作品集展示、基于时间线的故事。
模式 3:分层视差
多个视觉层(背景、中景、前景)以不同速度移动以营造深度感。
何时使用:氛围叙事、游戏/产品发布、深度能增强情感冲击的长篇叙事。
无障碍警告:视差会触发前庭功能障碍(头晕、恶心、偏头痛)。务必提供减少动态效果的降级方案;每页最多限制一个微妙的视差效果。
模式 4:多方向
将垂直滚动与水平区块或侧向时间线相结合。
适用场景:基于时间线的内容、视觉驱动的展示、通过惊喜感强化信息的非常规布局。
何时使用滚动叙事
适合的场景:
- 包含多媒体的长篇新闻报道
- 庆祝成就的品牌故事
- 展示功能的产品页面
- 按时间顺序/历史类内容
- 拆解为易消化片段的复杂叙事
- 需要深度了解的高考虑度产品
避免使用的情况:
- 缺乏强有力的视觉素材
- 时间/预算紧张(优秀的滚动叙事需要更多投入)
- 故事缺乏明确的时间顺序
- 内容简短
- 在低端设备上性能至关重要
需求探索问题
在实施之前,请与用户确认:
header: "Scrollytelling Pattern"
question: "What scrollytelling pattern fits your narrative?"
options:
- "Pinned narrative - text changes while visual stays fixed (NYT, Pudding.cool style)"
- "Progressive reveal - content fades in as you scroll down"
- "Parallax depth - layers move at different speeds (requires reduced-motion fallback)"
- "Step sequence - discrete sections with transitions between"
- "Hybrid - multiple patterns combined"
header: "Tech Stack"
question: "What's your frontend setup?"
options:
- "React + Tailwind"
- "React + CSS-in-JS"
- "Next.js"
- "Vue"
- "Vanilla JS"
- "Other"
header: "Animation Approach"
question: "Animation library preference?"
options:
- "CSS-only (scroll-timeline API, IntersectionObserver) - best performance"
- "GSAP ScrollTrigger - most powerful, cross-browser"
- "Framer Motion / Motion - React ecosystem"
- "Lenis + custom - smooth scroll"
- "No preference - recommend based on complexity"
技术实现(2025-2026)
技术选型指南
| 复杂度 | 推荐方案 | 包体积 |
|---|---|---|
| 简单揭示效果、进度条 | 原生 CSS scroll-timeline | 0 KB |
| 视口触发效果 | IntersectionObserver | 0 KB |
| 复杂时间线、固定元素 | GSAP ScrollTrigger | ~23 KB |
| React 项目 | Motion (Framer Motion) | ~32 KB |
| 平滑滚动 + 效果 | Lenis + GSAP | ~25 KB |
CSS 滚动驱动动画(原生 - 2025+)
浏览器支持:
- Chrome 115+:完全支持(自 2025 年 7 月起)
- Safari 26+:完全支持(自 2025 年 9 月起)
- Firefox:需要开启标志(
layout.css.scroll-driven-animations.enabled)
关键属性:
animation-timeline: scroll()- 将动画与滚动位置关联animation-timeline: view()- 将动画与元素可见性关联animation-range- 控制动画的开始/结束时机
示例 - 视口触发的淡入:
@supports (animation-timeline: scroll()) {
.reveal-on-scroll {
animation: reveal linear both;
animation-timeline: view();
animation-range: entry 0% entry 100%;
}
@keyframes reveal {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
}
示例 - 滚动关联的进度条:
.progress-bar {
animation: grow linear;
animation-timeline: scroll();
}
@keyframes grow {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
性能优势:Tokopedia 通过改用原生 CSS 滚动驱动动画,实现了 80% 的代码缩减,CPU 使用率从 50% 降至 2%。
IntersectionObserver 模式
适用于无需持续跟踪滚动的滚动触发效果:
const RevealOnScroll = ({ children, delay = 0 }) => {
const ref = useRef(null);
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
// Check reduced motion preference
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
setIsVisible(true);
return;
}
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsVisible(true);
observer.disconnect();
}
},
{ threshold: 0.1, rootMargin: '-50px' }
);
if (ref.current) observer.observe(ref.current);
return () => observer.disconnect();
}, []);
return (
<div
ref={ref}
style={{
opacity: isVisible ? 1 : 0,
transform: isVisible ? 'translateY(0)' : 'translateY(30px)',
transition: `all 0.6s ease ${delay}ms`,
}}
>
{children}
</div>
);
};
GSAP ScrollTrigger(复杂动画)
适用于固定区块、时间线编排以及跨浏览器可靠性:
const ScrollytellingSection = ({ steps }) => {
const containerRef = useRef(null);
const [activeStep, setActiveStep] = useState(0);
useEffect(() => {
// Check reduced motion preference
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
return;
}
const ctx = gsap.context(() => {
steps.forEach((_, index) => {
ScrollTrigger.create({
trigger: `.step-${index}`,
start: 'top center',
end: 'bottom center',
onEnter: () => setActiveStep(index),
onEnterBack: () => setActiveStep(index),
});
});
}, containerRef);
return () => ctx.revert();
}, [steps]);
return (
<section ref={containerRef} className="relative">
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{/* Text column - scrolls naturally */}
<div className="space-y-[100vh]">
{steps.map((step, i) => (
<div
key={i}
className={`step-${i} min-h-screen flex items-center transition-opacity duration-300 ${
activeStep === i ? 'opacity-100' : 'opacity-30'
}`}
>
<div className="max-w-md">
<h3 className="text-2xl font-bold mb-4">{step.title}</h3>
<p className="text-lg">{step.description}</p>
</div>
</div>
))}
</div>
{/* Visual column - sticky */}
<div className="relative hidden md:block">
<div className="sticky top-0 h-screen flex items-center justify-center">
<StepVisual step={activeStep} data={steps[activeStep]} />
</div>
</div>
</div>
</section>
);
};
适用于 React 的 Motion (Framer Motion)
import { motion, useScroll, useTransform } from 'motion/react';
const ScrollLinkedSection = () => {
const ref = useRef(null);
const { scrollYProgress } = useScroll({
target: ref,
offset: ['start end', 'end start'],
});
const opacity = useTransform(scrollYProgress, [0, 0.5, 1], [0, 1, 0]);
const scale = useTransform(scrollYProgress, [0, 0.5, 1], [0.8, 1, 0.8]);
return (
<section ref={ref} className="h-[200vh] relative">
<motion.div
className="sticky top-0 h-screen flex items-center justify-center"
style={{ opacity, scale }}
>
<h2 className="text-6xl font-bold">Scroll-Linked Content</h2>
</motion.div>
</section>
);
};
滚动进度 Hook
const useScrollProgress = (ref) => {
const [progress, setProgress] = useState(0);
useEffect(() => {
const element = ref.current;
if (!element) return;
const updateProgress = () => {
const rect = element.getBoundingClientRect();
const windowHeight = window.innerHeight;
// 0 when element top enters viewport, 1 when bottom exits
const start = rect.top - windowHeight;
const end = rect.bottom;
const current = -start;
const total = end - start;
setProgress(Math.max(0, Math.min(1, current / total)));
};
window.addEventListener('scroll', updateProgress, { passive: true });
updateProgress();
return () => window.removeEventListener('scroll', updateProgress);
}, [ref]);
return progress;
};
无障碍要求
无障碍是不可妥协的。如果实施不当,滚动叙事可能引发前庭功能障碍,并将键盘/屏幕阅读器用户排除在外。
关键 WCAG 标准
| 标准 | 要求 |
|---|---|
| SC 2.2.2 | 自动播放内容超过 5 秒需要提供暂停/停止/隐藏控件 |
| SC 2.3.3 | 用户触发的动画必须可以禁用 |
| SC 2.3.1 | 闪烁不得超过每秒 3 次 |
减少动态效果支持(强制)
始终尊重用户偏好:
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0s !important;
animation-iteration-count: 1 !important;
transition-duration: 0s !important;
scroll-behavior: auto !important;
}
}
const prefersReducedMotion = () =>
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
// In components - check before animating
useEffect(() => {
if (prefersReducedMotion()) {
// Show content immediately, skip animations
return;
}
// Set up animations
}, []);
安全动画指南
| 动画类型 | 安全性 |
|---|---|
| 淡入/淡出,0.5 秒以内 | 安全 |
| 简单变换 | 安全 |
| 视差滚动 | 会引发前庭问题 - 始终提供回退方案 |
| 俯冲、缩放 | 有问题 - 避免或提供回退方案 |
| 循环动画 | 认知过载 - 限制迭代次数 |
保持效果轻量:影响超过 1/3 视口的动画可能会让用户不堪重负。
键盘导航
- 为可滚动区域添加
tabindex="0" - 使用平滑滚动时,确保焦点跟随滚动目标
- 为主要区块提供跳转链接
- 滚动区域内不得存在键盘陷阱
屏幕阅读器注意事项
- 使用正确的标题层级(
<h1>到<h6>) - DOM 顺序必须与逻辑阅读顺序一致
- 使用 ARIA 实时区域处理动态内容更新
- 确保所有内容都存在于 DOM 中(即使初始视觉上隐藏)
性能最佳实践
应该做
- 使用
transform和opacity实现动画(GPU 加速) - 在动画元素上谨慎添加
will-change: transform - 在滚动监听器上使用
passive: true - 使用
position: sticky而非基于 JS 的固定定位 - 延迟加载图片/视频,直到需要时再加载
- 为多个元素使用单个 IntersectionObserver
- 在真实设备上测试,而不仅仅是在桌面端
不应该做
- 对
width、height、top、left做动画(会触发布局重算) - 过度使用
will-change(会增加内存占用) - 创建滚动监听器却不清理
- 忘记处理减弱动效偏好
- 在 sticky 元素的祖先元素上使用
overflow: hidden
性能目标
- 首次内容绘制:2.5 秒以内
- 滚动过程中保持 60fps
- 满足 Core Web Vitals 阈值
移动端考量
移动端用户占网络流量的 60% 以上。滚动叙事必须在移动端表现出色,否则可能会将大多数用户排除在外。
移动优先的设计理念
从移动端开始:“从移动端入手会迫使你把体验精简到最核心的部分,只保留必需品。这能提炼并聚焦内容。”(The Pudding)
先为移动端设计核心体验,再为桌面端增强——而不是反过来。这种方法:
- 迫使你只做必要内容的决策
- 提高开发效率
- 如果桌面端功能相似,代码量会更少
视口单位:vh vs svh vs dvh vs lvh
移动端浏览器在滚动时会切换导航栏,破坏传统的 100vh 布局。
| 单位 | 定义 | 何时使用 |
|---|---|---|
vh | 大视口(浏览器 UI 隐藏) | 仅作为旧版回退 |
svh | 小视口(浏览器 UI 可见) | 约 90% 的布局使用(推荐) |
lvh | 大视口(浏览器 UI 隐藏) | 模态框/浮层,用于最大化空间 |
dvh | 动态视口(不断变化) | 谨慎使用——会导致布局抖动 |
重要警告:“我最初以为‘动态视口单位是未来’,于是每个元素都用 dvh。这是个错误。持续的布局偏移让人感觉像是坏了。”
实现模式:
.full-height-section {
height: 100vh; /* Fallback for older browsers */
height: 100svh; /* Modern solution - small viewport */
}
/* Progressive enhancement */
@supports (height: 100svh) {
:root {
--viewport-height: 100svh;
}
}
JavaScript 替代方案(The Pudding 的推荐):
function setViewportHeight() {
const vh = window.innerHeight;
document.documentElement.style.setProperty('--vh', `${vh}px`);
}
window.addEventListener('resize', setViewportHeight);
setViewportHeight();
.section {
height: calc(var(--vh) * 100);
}
触摸滚动物理特性
惯性滚动:触摸释放后内容继续滚动,自然减速。iOS 和 Android 有不同的摩擦曲线——iOS 感觉更“滑”。
对 iOS 至关重要:
.scroll-container {
overflow-y: auto;
-webkit-overflow-scrolling: touch; /* iOS momentum - still needed for pre-iOS 13 */
}
性能提示:在 iOS 上,滚动事件在惯性结束时触发,而不是过程中。使用 IntersectionObserver 而非滚动监听器来检测步骤。
防止手势冲突
下拉刷新冲突:
/* Disable PTR but keep bounce effects */
html {
overscroll-behavior-y: contain;
}
/* Or disable completely */
html {
overscroll-behavior-y: none;
}
模态框中的滚动链:
.modal-content {
overflow-y: auto;
overscroll-behavior: contain; /* Prevents scrolling parent when modal hits boundary */
}
水平滑动冲突(浏览器后退/前进):
.horizontal-carousel {
touch-action: pan-y pinch-zoom; /* Allow vertical scroll & zoom, block horizontal */
}
被动事件监听器
Chrome 56+ 默认将触摸监听器设为被动,以实现 60fps 滚动。监控用途使用被动监听器,只有在必须 preventDefault() 时才使用非被动:
// ✅ Monitoring scroll (passive - default, best performance)
document.addEventListener('touchstart', trackTouch, { passive: true });
// ⚠️ Only when you MUST prevent default (e.g., custom swipe)
carousel.addEventListener('touchmove', handleSwipe, { passive: false });
优先使用 CSS 而非 JavaScript:
/* Better than JavaScript preventDefault */
.element {
touch-action: pan-y pinch-zoom;
}
移动端的滚动吸附
滚动吸附在移动端配合 mandatory 效果良好(在触摸设备上避免使用 proximity):
.scroll-container {
scroll-snap-type: y mandatory;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.section {
scroll-snap-align: start;
min-height: 100svh;
}
/* Accessibility */
@media (prefers-reduced-motion: reduce) {
.scroll-container {
scroll-snap-type: none;
scroll-behavior: auto;
}
}
警告:如果内容可能溢出视口,切勿使用 mandatory——用户将无法滚动查看内容。
触摸无障碍
最小触摸目标尺寸:
| 标准 | 尺寸 | 何时 |
|---|---|---|
| WCAG 2.5.8 (AA) | 24×24px | 最低合规要求 |
| WCAG 2.5.5 (AAA) | 44×44px | 最佳实践 |
| Apple iOS | 44×44pt | 推荐 |
| Android | 48×48dp | 推荐 |
在不改变视觉尺寸的情况下扩大触摸区域:
.small-button {
width: 24px;
height: 24px;
padding: 10px; /* Creates 44×44px touch target */
}
始终为仅手势操作提供按钮替代方案:
<div class="item">
<span>Content</span>
<button aria-label="Delete">Delete</button>
</div>
浏览器特定怪癖
iOS Safari:
/* Position sticky requires no overflow on ancestors */
.parent {
/* overflow: hidden; ❌ Breaks sticky on iOS */
}
.sticky {
position: -webkit-sticky; /* Prefix still needed */
position: sticky;
top: 0;
}
/* Preventing body scroll in modals requires JavaScript on iOS */
/* CSS overflow: hidden doesn't work on body */
// iOS modal scroll lock
function lockScroll() {
document.body.style.position = 'fixed';
document.body.style.top = `-${window.scrollY}px`;
}
Chrome Android:
/* Disable pull-to-refresh */
body {
overscroll-behavior-y: none;
}
响应式布局策略
并排 → 堆叠模式:
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{/* On mobile: stacked, full-width */}
{/* On desktop: side-by-side sticky */}
<div className="space-y-[50vh] md:space-y-[100vh]">
{/* Text steps - shorter spacing on mobile */}
</div>
<div className="hidden md:block">
{/* Sticky visual - hidden on mobile, shown on desktop */}
</div>
</div>
同步 CSS 和 JS 断点:
const breakpoint = '(min-width: 800px)';
const isDesktop = window.matchMedia(breakpoint).matches;
if (isDesktop) {
initScrollama(); // Complex scrollytelling
} else {
initStackedView(); // Simple stacked layout
}
// Listen for breakpoint changes
window.matchMedia(breakpoint).addEventListener('change', (e) => {
if (e.matches) {
initScrollama();
} else {
initStackedView();
}
});
移动端替代方案:
- 将粘性图形替换为文本段落之间的内联图形
- 使用更简单的显现动画,而不是复杂的视差效果
- 堆叠静态图片,并配以滚动触发的说明文字
- 考虑滚动叙事是否真的合适
移动端性能策略
目标:60fps(每帧 16.7ms)
仅使用硬件加速属性:
.animate {
/* ✅ Good - GPU accelerated */
transform: translateY(100px);
opacity: 0.5;
/* ❌ Bad - triggers layout recalculation */
/* top: 100px; width: 200px; margin: 20px; */
}
谨慎使用 will-change:
/* Only on elements about to animate */
.about-to-animate {
will-change: transform;
}
/* Remove when animation completes */
.animation-complete {
will-change: auto;
}
警告:过多的合成层会损害移动端性能。不要对一切都应用 will-change。
使用 requestAnimationFrame 节流滚动处理程序:
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
requestAnimationFrame(() => {
updateAnimation();
ticking = false;
});
ticking = true;
}
}, { passive: true });
更好:使用 IntersectionObserver(无需滚动事件):
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
animateElement(entry.target);
}
});
});
何时在移动端简化或放弃滚动叙事
在以下情况下保留滚动叙事:
- 转场对叙事确实有意义
- 空间移动或时间变化是理解内容的核心
- 可以达到性能目标(60fps,加载时间 <3s)
- 测试表明移动端用户能够成功理解内容
在以下情况下简化滚动叙事:
- 性能可接受,但动画并非必要
- 某些复杂性是锦上添花,但并非必需
- 桌面端体验更丰富,但移动端应保持可用
在以下情况下放弃滚动叙事:
- 在中端设备上无法解决性能问题
- 移动端用户在测试中感到困惑或沮丧
- 开发时间线不允许进行适当优化
- 内容在更简单的堆叠格式下同样有效
- 动画只是装饰性的,而非有意义的
“保留滚动动画最重要的理由,是转场确实有意义,而不只是为了让页面更抓眼球。”(The Pudding)
移动端测试清单
设备覆盖:
- iPhone(最近 2 款型号)- Safari
- iPhone - Chrome
- iPad - Safari(竖屏与横屏)
- Android 旗舰机 - Chrome
- Android 中端机 - Chrome(对性能至关重要)
- Android 平板 - Chrome
视口测试:
- 地址栏隐藏/显示过渡流畅
- 滚动过程中没有布局跳动
- 固定元素保持正确定位
- svh/dvh 单位表现符合预期
性能测试:
- 滚动过程中保持 60fps(使用 Chrome DevTools FPS 计量器)
- 没有卡顿的动画
- 图片正确懒加载
- 长时间会话中内存不会泄漏
- 使用 CPU 节流进行测试(DevTools 中 4 倍减速)
交互测试:
- 触摸滚动感觉自然(惯性)
- 没有意外交互
- 如需要,禁用下拉刷新
- 滚动链接行为正确
无障碍测试:
- 在启用减少动态效果时正常工作
- 触摸目标最小 44×44px
- 为所有手势提供按钮替代方案
- 屏幕阅读器(VoiceOver/TalkBack)可以导航
关键:Chrome DevTools 移动端模拟器并不能准确模拟浏览器 UI 行为。请在真实设备上测试,或使用 BrowserStack/Sauce Labs
应避免的反模式
| 反模式 | 问题 | 解决方案 |
|---|---|---|
| 滚动劫持 | 覆盖自然滚动,破坏无障碍性 | 保留原生滚动行为 |
| 文本与图形冲突 | 用户无法在观看动画的同时阅读文本 | 将文本和动画区域分开 |
| 动画过载 | 分散对内容的注意力,造成疲劳 | 克制——并非每个部分都需要效果 |
| 没有长度指示 | 用户不知道需要投入多少 | 添加进度指示器 |
| 缺少回退方案 | 对无 JS 或减少动态效果的用户会失效 | 渐进增强 |
| 忽视移动端 | 排除了 60% 的用户 | 移动优先设计 |
| 节奏不佳 | 内容显现太快或太慢 | 与真实用户一起测试 |
实施工作流
- 理解叙事 - 你要讲述什么故事?顺序是什么?
- 选择模式 - 固定、渐进、视差还是混合?
- 规划无障碍 - 减少动态效果的用户将如何体验?
- 选择技术 - 根据复杂度选择原生 CSS、GSAP、Motion
- 搭建结构 - 先构建 HTML/组件结构
- 添加滚动机制 - 实现跟踪(IntersectionObserver、ScrollTrigger 等)
- 连接动画 - 将滚动状态与视觉变化关联
- 添加减少动态效果的降级方案 - 内容应在无动画时仍可正常使用
- 性能审计 - 检查卡顿并优化
- 跨设备测试 - 移动端、平板、桌面端、不同浏览器
- 无障碍测试 - 键盘导航、屏幕阅读器、减少动态效果
输出
收集需求后,直接在代码库中实现滚动叙事体验。提供:
- 带滚动跟踪的组件结构
- 带减少动态效果处理的动画/过渡逻辑
- 响应式调整(移动优先)
- 无障碍降级方案
- 性能优化
- 测试建议
值得参考的著名示例
- NYT "Snow Fall" - 起源故事、多媒体整合
- Pudding.cool - 带音画同步的数据新闻
- National Geographic "Atlas of Moons" - 教育性、响应式设计
- BBC "Partition of India" - 带多媒体的历史叙事
- Firewatch 网站 - 多层视差营造氛围
来源
研究基于 100+ 个来源,包括 EU Data Visualization Guide、MDN、GSAP 文档、W3C WCAG、A List Apart、Smashing Magazine、The Pudding、CSS-Tricks,以及关于滚动叙事有效性的学术研究。