使用GSAP构建分层缩放滚动效果
学习如何使用GSAP的ScrollSmoother和ScrollTrigger插件重现Telescope网站的平滑分层缩放滚动动画,实现电影般深度感的滚动体验。
浮动图片网格
HTML结构
在开始动画之前,我们先从基础开始。布局可能看起来是解构的,但需要保持简单和可预测。
1
2
3
4
5
6
7
|
<div class="section">
<div class="section__images">
<img src="./img-1.webp" alt="Image" />
<img src="./img-2.webp" alt="Image" />
<!-- 更多图片 -->
</div>
</div>
|
样式设计
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
.section__images {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
perspective: 100vh;
img {
position: absolute;
width: 10vw;
@media (max-width: 768px) {
width: 20vw;
}
}
}
|
在样式设计方面,有几个重要注意事项。我们设置了一个全屏区域来包含所有浮动图片。该区域使用perspective值启用Z轴动画,为构图添加深度。在此区域内,每个图片都绝对定位以创建有机、分散的排列。
动画实现
首先,我们使用ScrollSmoother插件引入细微的滚动惯性,使滚动体验更加流畅精致。
1
2
3
4
5
6
7
|
const scroller = ScrollSmoother.create({
wrapper: ".wrapper",
content: ".content",
smooth: 1.5,
effects: true,
normalizeScroll: true
})
|
我们只需要一个GSAP时间轴来处理整个动画。让我们使用ScrollTrigger插件开始设置。
1
2
3
4
5
6
7
8
9
|
this.timeline = gsap.timeline({
scrollTrigger: {
trigger: this.dom,
start: "top top",
end: "bottom top",
scrub: true,
pin: true
}
})
|
接下来,我们通过沿Z轴移动小图片来为它们添加动画。为了使运动感觉更动态,我们添加了stagger,在每张图片之间引入小延迟。
1
2
3
4
5
6
7
8
9
|
this.timeline.to(this.smallImages, {
z: "100vh",
duration: 1,
ease: "power1.inOut",
stagger: {
amount: 0.2,
from: "center"
}
})
|
主视觉和分离文本
现在我们已经构建了浮动图片网格,是时候专注于动画的核心部分——主图片和分开移动以显示它的文本。
HTML结构
1
2
3
4
5
|
<div class="section__media">
<div class="section__media__back">
<img src="./img-big.jpg" alt="Image" />
</div>
</div>
|
样式设计
使用绝对定位将大图片添加为全尺寸覆盖,并定义一个CSS变量--progress,稍后我们将用它来控制动画。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
.section__media {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
transform: scale(var(--progress));
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
|
对于图片动画,我们采用稍微不同的方法。我们不直接使用GSAP动画scale属性,而是在整个时间轴中动画一个名为--progress的CSS变量。
1
2
3
4
|
onUpdate: (self) => {
const easedProgress = gsap.parseEase("power1.inOut")(self.progress)
this.dom.style.setProperty("--progress", easedProgress)
}
|
接下来,我们添加文本元素,它分为两部分:一部分向左滑动,另一部分向右移动。
1
2
3
4
|
<h1>
<span class="left">for the</span>
<span class="right">planet</span>
</h1>
|
现在我们只需要在CSS中使用--progress变量来动画两侧的文本部分。
1
2
3
4
5
6
7
|
.left {
transform: translate3d(calc(var(--progress) * (-66vw + 100%) - 0.5vw), 0, 0);
}
.right {
transform: translate3d(calc(var(--progress) * (66vw - 100%)), 0, 0);
}
|
分层缩放和深度效果
前端图片层
我们首先在结构中添加"前端"图片,它们是背景图片的简单副本。这些图层将帮助我们创建添加深度和运动的拖尾缩放效果。
1
2
3
4
|
<div class="section__media__front front-1">
<img src="./img-big.jpg" alt="Image" />
</div>
<!-- 更多前端图片层 -->
|
遮罩应用
我们将创建并添加主体(本例中为螃蟹)的遮罩,使其看起来像是从背景中弹出。
1
2
3
4
5
6
7
|
.section__media__front {
img {
mask-image: url(./mask.png);
mask-position: 50% 50%;
mask-size: cover;
}
}
|
分层缩放
我们逐步缩放每个图片层以创建深度感。第一个元素保持其原始大小,而每个后续图层稍微小一些,给人以它们进一步移动到背景中的印象。
1
2
3
4
5
6
|
.front-1 { transform: scale(1); }
.front-2 { transform: scale(0.85); }
.front-3 { transform: scale(0.6); }
.front-4 { transform: scale(0.45); }
.front-5 { transform: scale(0.3); }
.front-6 { transform: scale(0.15); }
|
最后,我们只需要在时间轴中再添加一个步骤,将所有图片层带回到其原始比例(scale: 1)。
1
2
3
4
5
6
|
this.timeline.to(this.frontImages, {
scale: 1,
duration: 1,
ease: "power1.inOut",
delay: .1,
}, 0.4)
|
模糊效果
为了使效果更加精致,我们可以在开始时为每个图层添加细微的模糊,然后在时间轴播放时将其动画消失。
1
2
3
|
.section__media__front {
filter: blur(2px);
}
|
1
2
3
4
5
6
7
8
9
10
|
this.timeline.to(this.frontImages, {
duration: 1,
filter: "blur(0px)",
ease: "power1.inOut",
delay: .4,
stagger: {
amount: 0.2,
from: "end"
}
}, 0.6)
|
缩放和模糊动画相结合,分层缩放效果感觉丰富而沉浸。每个图层和谐移动,为动画提供深度和流畅性,同时保持整体体验的平滑和视觉平衡。
最终效果
这是实际运行的最终结果。缩放、模糊和平滑滚动的组合创建了干净的分层运动,感觉既自然又视觉上吸引人。细微的深度变化给人以3D场景在滚动时栩栩如生的印象,所有这些都只用几个精心定时的动画构建而成。