关于 cinematic-scroll-storytelling
面向网页设计与开发者的电影感滚动叙事指南,使用 GSAP、ScrollTrigger 和 Lenis 组织预加载、文字揭示、视差及卡片堆叠。包含构建顺序与移动端、减少动态效果的检查建议。
主要功能
- 01预加载与主视觉入场
- 02分词文字揭示
- 03滚动触发与滚动联动动画
- 04粘性卡片堆叠
- 05视差与固定场景
适用场景
- 01制作逐段展开的产品落地页
- 02构建作品集的粘性项目卡片
- 03为编辑式网页设计滚动叙事
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mengto/ skills --skill cinematic-scroll-storytelling安装后在 Claude Code 中输入/cinematic-scroll-storytelling确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
REFERENCES.mddemo/index.htmlPROMPT.md详细介绍
电影感滚动叙事
适用场景
- 页面应像高级编辑式故事一样,随着用户滚动逐步展开。
- 用户提到滚动驱动叙事、滚动联动动画、粘性卡片堆叠、视差、分段文本、预加载器或电影感推进效果。
- 作品集、工作室、产品或落地页需要逐段呈现,并展现层次感。
- 实现可以使用 GSAP、ScrollTrigger 和 Lenis。
效果术语
- 滚动驱动叙事:随着滚动,页面各个部分按顺序逐步呈现。
- 滚动联动动画:进度直接与滚动关联,使用
scrub。 - 滚动触发动效:当某个部分进入视口时,动画开始播放。
- 错落呈现:文字、行、卡片或元素以微小延迟逐一进入。
- 渐进呈现:透明度、缩放、模糊、裁剪或位置随滚动进度变化。
- 粘性卡片堆叠:粘性卡片逐层叠加、缩放,并在下一张卡片出现时后退。
- 视差滚动:背景层和前景层以不同速度移动。
- 滚动平滑控制:动画通过
scrub: true或scrub: 1跟随滚动条。 - 动态排版:通过遮罩呈现分段文本动效,通常逐词或逐行播放。
- 预加载器:开场加载画面、进度条和入场过渡。
目标观感
- 奢华编辑风格网站。
- 高端创意工作室作品集。
- Apple 级别的动效润色。
- 现代 Awwwards 式交互风格。
- 沉浸式电影感落地页。
避免:
- 弹跳、弹性或弹簧式动效。
- 夸张的缩放跳变。
- 炫目的游戏风格效果。
- 同时出现过多滚动效果。
- 让页面难以阅读的滚动劫持。
核心技术栈
npm i gsap lenis
import Lenis from "lenis";
import "lenis/dist/lenis.css";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (!reduceMotion) {
const lenis = new Lenis({
lerp: 0.08,
smoothWheel: true,
wheelMultiplier: 0.9,
});
lenis.on("scroll", ScrollTrigger.update);
gsap.ticker.add((time) => {
lenis.raf(time * 1000);
});
gsap.ticker.lagSmoothing(0);
}
window.addEventListener("load", () => ScrollTrigger.refresh());
动效参数
- 入场缓动:
power3.out或power4.out。 - 滚动平滑控制场景:使用
ease: "none",并将scrub设为0.8至1.4。 - 文本呈现时长:
0.8s至1.1s。 - 卡片呈现时长:
0.9s至1.2s。 - 逐词错落间隔:
0.035s至0.07s。 - 逐行错落间隔:
0.08s至0.14s。 - 卡片错落间隔:
0.06s至0.1s。 - 呈现偏移:
y: 24至48。 - 模糊:
4px至10px,然后变为0px。 - 粘性卡片缩放层次:从
1降至0.92。
页面结构
- 预加载器:黑屏、进度条、品牌/标题、开场淡入。
- 首屏:图像视差、遮罩标题呈现、微妙的滚动提示。
- 引言:逐词动态排版。
- 故事部分:滚动触发淡入上移、模糊淡入和裁剪呈现。
- 最新项目:粘性卡片堆叠,带缩放和层次感。
- 画廊或成果展示:滚动平滑控制的横向呈现或渐进式呈现。
- 页脚:视差呈现或缓慢向上的衔接效果。
标记模式
<div class="preloader" data-preloader>
<div class="preloader__bar" data-preloader-bar></div>
</div>
<main>
<section class="hero" data-parallax-section>
<img data-parallax-layer data-speed="-0.18" src="/hero.jpg" alt="">
<h1 data-split-reveal>Design that unfolds with cinematic restraint.</h1>
</section>
<section data-story-section>
<p data-split-reveal="words">Every block arrives with quiet intent.</p>
</section>
<section class="project-stack" data-sticky-stack>
<article data-stack-card>Project One</article>
<article data-stack-card>Project Two</article>
<article data-stack-card>Project Three</article>
</section>
<footer data-footer-parallax>...</footer>
</main>
预加载器序列
使用预加载器营造电影感基调,然后衔接到首屏呈现。
function initPreloader() {
const loader = document.querySelector("[data-preloader]");
const bar = document.querySelector("[data-preloader-bar]");
if (!loader) return Promise.resolve();
if (reduceMotion) {
loader.remove();
return Promise.resolve();
}
return new Promise((resolve) => {
const tl = gsap.timeline({
defaults: { ease: "power3.out" },
onComplete: () => {
loader.remove();
resolve();
},
});
tl.fromTo(bar, { scaleX: 0, transformOrigin: "left" }, { scaleX: 1, duration: 1.1 })
.to(loader, { yPercent: -100, duration: 0.9, ease: "power4.inOut" }, "+=0.15");
});
}
分段文本呈现
使用遮罩溢出容器。避免拆分包含链接或有意义的行内标记的文本。
function splitWords(element) {
if (element.dataset.splitReady === "true") return;
const text = element.textContent || "";
const parts = text.split(/(\s+)/);
element.textContent = "";
element.setAttribute("aria-label", text.trim());
parts.forEach((part) => {
if (!part.trim()) {
element.appendChild(document.createTextNode(part));
return;
}
const mask = document.createElement("span");
const word = document.createElement("span");
mask.className = "split-word-mask";
word.className = "split-word";
word.textContent = part;
mask.setAttribute("aria-hidden", "true");
mask.appendChild(word);
element.appendChild(mask);
});
element.dataset.splitReady = "true";
}
function initSplitReveals() {
if (reduceMotion) {
gsap.set("[data-split-reveal]", { autoAlpha: 1 });
return;
}
gsap.utils.toArray("[data-split-reveal]").forEach((element) => {
splitWords(element);
const words = element.querySelectorAll(".split-word");
gsap.fromTo(
words,
{ yPercent: 110, autoAlpha: 0, filter: "blur(8px)" },
{
yPercent: 0,
autoAlpha: 1,
filter: "blur(0px)",
duration: 0.95,
ease: "power4.out",
stagger: 0.05,
scrollTrigger: {
trigger: element,
start: "top 82%",
once: true,
},
}
);
});
}
.split-word-mask {
display: inline-block;
overflow: hidden;
vertical-align: top;
}
.split-word {
display: inline-block;
will-change: transform, opacity, filter;
}
滚动触发的呈现
将这些效果用于常规页面部分。动画应只播放一次,效果应沉稳自然,不要显得抖动。
function initSectionReveals() {
if (reduceMotion) {
gsap.set("[data-story-section], [data-reveal-item]", { autoAlpha: 1, clearProps: "all" });
return;
}
gsap.utils.toArray("[data-story-section]").forEach((section) => {
const items = section.querySelectorAll("[data-reveal-item]");
const targets = items.length ? items : section.children;
gsap.fromTo(
targets,
{ y: 36, autoAlpha: 0, filter: "blur(8px)" },
{
y: 0,
autoAlpha: 1,
filter: "blur(0px)",
duration: 1,
ease: "power4.out",
stagger: 0.08,
scrollTrigger: {
trigger: section,
start: "top 82%",
once: true,
},
}
);
});
}
滚动联动的渐进效果
使用平滑控制时间线实现电影感推进。保持平滑控制动画线性,并让滚动位置控制时间。
function initProgressionScenes() {
if (reduceMotion) return;
gsap.utils.toArray("[data-progress-scene]").forEach((scene) => {
const media = scene.querySelector("[data-progress-media]");
const copy = scene.querySelectorAll("[data-progress-copy]");
gsap.timeline({
scrollTrigger: {
trigger: scene,
start: "top top",
end: "+=140%",
scrub: 1.1,
pin: true,
anticipatePin: 1,
},
})
.fromTo(media, { scale: 1.08 }, { scale: 1, ease: "none" })
.fromTo(copy, { autoAlpha: 0, y: 40 }, { autoAlpha: 1, y: 0, stagger: 0.15, ease: "none" }, 0.15);
});
}
粘性卡片堆叠
使用 position: sticky 实现布局,并使用 ScrollTrigger 实现分层缩放和深度效果。后续卡片出现时,前面的卡片应逐渐后退。
[data-sticky-stack] {
position: relative;
}
[data-stack-card] {
position: sticky;
top: 12vh;
transform-origin: center top;
will-change: transform, opacity;
}
function initStickyCardStack() {
if (reduceMotion) return;
gsap.utils.toArray("[data-sticky-stack]").forEach((stack) => {
const cards = gsap.utils.toArray(stack.querySelectorAll("[data-stack-card]"));
cards.forEach((card, index) => {
const nextCard = cards[index + 1];
if (!nextCard) return;
gsap.to(card, {
scale: 0.92 + index * 0.015,
autoAlpha: 0.72,
y: -24,
ease: "none",
scrollTrigger: {
trigger: nextCard,
start: "top 78%",
end: "top 24%",
scrub: true,
invalidateOnRefresh: true,
},
});
});
});
}
视差
在首屏图像、背景层和页脚呈现中使用视差。移动距离应保持较小。
function initParallax() {
if (reduceMotion) return;
gsap.utils.toArray("[data-parallax-layer]").forEach((layer) => {
const speed = Number(layer.dataset.speed || -0.16);
const section = layer.closest("[data-parallax-section]") || layer;
gsap.to(layer, {
y: () => window.innerHeight * speed,
ease: "none",
scrollTrigger: {
trigger: section,
start: "top bottom",
end: "bottom top",
scrub: 1,
invalidateOnRefresh: true,
},
});
});
}
页脚视差呈现:
function initFooterReveal() {
if (reduceMotion) return;
const footer = document.querySelector("[data-footer-parallax]");
if (!footer) return;
gsap.fromTo(
footer,
{ yPercent: -12, autoAlpha: 0.85 },
{
yPercent: 0,
autoAlpha: 1,
ease: "none",
scrollTrigger: {
trigger: footer,
start: "top bottom",
end: "top 45%",
scrub: 1,
},
}
);
}
构建顺序
- 先构建静态页面。
- 添加预加载器和首屏入场效果。
- 添加分段文本呈现。
- 添加逐段呈现效果。
- 添加粘性卡片堆叠推进效果。
- 添加视差层。
- 仅在叙事需要时添加平滑控制的固定场景。
- 添加减少动效和触屏设备的备用方案。
- 在桌面端和移动端进行浏览器质量检查。
提示词模板
Create a cinematic scroll-driven landing page with smooth Lenis scrolling, GSAP ScrollTrigger animations, staggered text reveals, sticky card stack progression, parallax backgrounds, scroll-scrubbed transitions, section-by-section storytelling, and an immersive preloader animation. Use layered depth, scaling transitions, progressive opacity changes, and smooth viewport-triggered motion for a premium editorial experience.
QA 检查清单
- 禁用 JavaScript 后,内容仍然可读。
- 减少动态效果的用户看到的是静态内容,且没有平滑滚动层。
- 滚动触发的内容只播放一次。
- 有意使用
scrub实现与滚动关联的场景。 - 固定卡片不会与页脚重叠,也不会导致页面无法滚动。
- 视差移动保持轻微,不影响可读性。
- 即使图片加载缓慢,预加载器也能可靠退出。
- 图片、字体或布局发生变化后,运行
ScrollTrigger.refresh()。 - 如果性能下降,移动端应简化固定效果或取消固定效果。
评论
支持 Markdown