内含实验性功能,目前仅 Chrome Canary (115以上)版本浏览器支持此特性
前言
Scroll-driven animations 顾名思义,就是由滚动驱动的动画,是一种常见的交互模式,在提升网站的用户体验方面,扮演着非常重要的角色,其应用场景比较广泛,例如:
- 网站导航栏根据滚动条向下滚动的进度逐步缩小尺寸、最终固定尺寸和位置
- TOC根据滚动位置高亮显示
- 容器边缘显示含义为”滚动查看更多“的阴影效果
- …
通常,我们需要利用JavaScript监听容器的 scroll
事件,并且拿到 scrollTop
等值来进行一系列的计算。这样做比较复杂,而且频繁获取 scrollTop
的值会因为频繁的回流重绘导致页面卡顿,动画不流畅的问题。
有没有一种更简单、高效率的方式来实现呢?当然有,我们先来看一个 Demo,演示一下效果。
Demo
效果图:
体验链接(请使用 Chrome Canary (115以上)版本):
codesandbox.io/s/scroll-dr…
可以看到,封面图随着页面滚动在逐渐缩小,并最终固定在顶部。
以上这个Demo就是纯CSS实现,没有使用任何JavaScript,接下来让我们一起看看是怎么实现的。
实现方式
animation-timeline
实现Demo效果的关键属性就是: animation-timeline(这是一个实验性的功能)。
默认情况下,CSS动画运行都是使用的 DocumentTimeline,简单来说就是页面加载完成后就开始运行,@keyframes
内所定义的进度是跟着自然时间走的。
而 Scroll-driven Animation Spec 定义了两种新的 timeline,可以指定动画在元素滚动条滚动时运行,@keyframes
的进度也就跟着滚动进度进行。
- Scroll Progress Timeline: 简称 scroll-timeline,代表容器已滚动的距离,从0%到100%
- View Progress Timeline: 简称 view-timeline,代表一个在容器内的元素的相对于滚动距离的相对位置,从0%到100%
我们的 Demo 中使用的就是 scroll-timeline:
/** 部分样式代码 */
/** 定义动画过程 */
@keyframes sticky-header-move-and-size {
from {
background-position: 50% 0;
height: 100vh;
font-size: calc(4vw + 1em);
}
to {
background-position: 50% 100%;
background-color: #0b1584;
height: 10vh;
font-size: 2em;
}
}
#header {
/** 指定 animation-name、animation-timing-function、animation-fill-mode */
animation: sticky-header-move-and-size linear forwards;
/** 指定 timeline */
animation-timeline: scroll();
animation-range: 0vh 90vh;
}
P.S. animation-timeline 必须写在简写 animation 属性之后,不然会被重置为默认值 auto
animation-range
值得注意的是,还有一个新的属性:animation-range(这是一个实验性的功能)。
这个属性定义了动画在时间轴上的附加范围的起始和结束位置,即动画将在时间轴的哪个位置开始和结束。Demo是缩写写法,还可以通过以下两个子属性来设置动画的起始和结束位置:
- animation-start:指定动画的起始位置,可以使用百分比、时间值或关键字来表示。
- animation-end:指定动画的结束位置,可以使用百分比、时间值或关键字来表示。
在Demo中,animation-range
设置的是从 scoll-timeline 的 0vh 开始动画,直到滚动到 90vh 的时候停止。
scroll()
上一个Demo中,将 animation-timeline 的值设置为了 CSS 函数 scroll()(这是一个实验性的功能)
scroll()
函数可接受一个<scroller>
和一个<axis>
参数
<scroller>
参数可以接受以下值:
nearest
:使用最近的祖先滚动容器(默认值)root
:使用文档视口作为滚动容器self
:使用元素本身作为滚动容器
<axis>
参数可以接受以下值:
block
:使用滚动容器的滚动进度(默认值)inline
:使用行内滚动条的滚动进度y
:使用滚动容器的 y 轴上的滚动进度x
:使用滚动容器的 x 轴上的滚动进度例如
scroll(root x)
表示使用最外层的x轴的滚动条的滚动进度
除了使用 scroll()
函数指定滚动容器,还可以使用 scroll-timeline
属性,将滚动容器的 scroll-timeline 赋值给 CSS 变量,然后在需要用到的地方给到 animation-timeline
属性;
下面是一个使用x轴滚动条进度展示进度条的示例:
体验链接(请使用 Chrome Canary (115以上)版本):
codesandbox.io/s/scroll-dr…
可以看到顶部的进度条跟随容器的滚动条进度变化而变化。实现的HTML结构为:
<div class="container" style="--num-images: 5;">
<div class="pics">
<div class="progress"></div>
<div class="pic">Pic-1</div>
<div class="pic">Pic-2</div>
<div class="pic">Pic-3</div>
<div class="pic">Pic-4</div>
<div class="pic">Pic-5</div>
</div>
</div>
部分CSS代码:
@keyframes progress-grow {
to {
transform: scaleX(1);
}
}
.pics {
/** 可简写为 scroll-timeline: --picx-scroll x; */
scroll-timeline-name: --pics-scroll;
scroll-timeline-axis: x;
}
.progress {
animation: auto progress-grow linear forwards;
/** 使用前面定义的 scroll-timeline */
animation-timeline: --pics-scroll;
}
由于HTML结构、定位等原因,这里利用 scroll-timeline
属性将图片容器 .pics
的 scroll-timeline 赋值给变量 --pics-scroll
,并且指定是 x 轴的滚动条;然后给进度条.progress
使用,从而实现的以上效果。
总结
利用 CSS 的 animation-timeline 可以非常方便的实现一些平时需要使用 JavaScript 才能实现的效果,而且理论上性能会更好,动画更加丝滑。这对于打工人来说又提升了不少的效率,希望可以尽快稳定下来并且各个浏览器厂商都跟进实现吧。
本文仅简单介绍了 scroll-timeline 的一个小的应用,更多玩法以及 view-timeline 的使用方法,大家可以参考更多示例。