Featured image of post 记录一个纯CSS实现滚动驱动动画的效果

记录一个纯CSS实现滚动驱动动画的效果

使用sticky与animation-timeline实现元素固定的滚动驱动动画

文章在个人网站中发布,原文链接:记录一个纯CSS实现滚动驱动动画的效果

先看一下这个简单的案例

代码:

你也可以直接划到下边看效果。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<div class="out-cont">
    <!-- 最外层元素,用于框定元素需要固定的距离 -->
    <div class="inner-cont">
        <!-- 内层元素,用于固定元素的位置 -->
        <div class="animation-item">
            <!-- 播放动画的元素内容 -->
        </div>
    </div>
</div>
<style>
.out-cont {
/* 最外层元素,用于框定元素需要固定的距离,这里表示元素将被固定两个屏幕的高度 */
    width: 100%;
    height: 200vh;
    position: relative;
}
.inner-cont {
/* 内层元素,用于固定元素的位置,使用sticky使元素在范围内固定 */
    width: 100%;
    height: 50vh;
    position: sticky;
    top: 25vh;
    display: flex;
    align-items: center;
    justify-content: center;
}
.animation-item{
/* 播放动画的元素内容,使用animation-timeline实现滚动驱动动画 */
    width: 20%;
    aspect-ratio: 1;
    background-color: #2E74B5;
    animation: move 1s linear forwards;
    animation-timeline: view();
    animation-range: contain;
}
@keyframes move {
    0% {
        transform: rotate(0deg);
        border-radius: 8%;
    }
    50% {
        transform: rotate(360deg);
        border-radius: 50%;
    }
    100% {
        border-radius: 8%;
        transform: rotate(720deg);
    }
}
</style>

效果:

.out-cont
.inner-cont
视口

sticky:粘性布局

对于前端码农来说这个玩意并不陌生,所以就只简单介绍一下:
position:sticky是介于position:relative与position:fixed之间的一种布局方式。当父元素出现在屏幕中时它表现得像fixed,会把自己固定在屏幕上。当父元素出现在屏幕外时它表现得像relative,会按照正常的文档流进行布局,被父容器带走。
关于这个案例是如何使用sticky实现类似元素固定的效果的,上面效果示例右边有一个简单的展示,可以帮助理解。而且这个展示也是纯css的哦。

sticky元素的所有父级都不能设置overflow:hidden,这样会使sicky无效,实在需要可以使用overflow:clip代替。

animation-timeline:滚动驱动动画

这是本案例的核心,元素上使用了这个属性,@keyframes定义的css动画将不会自动播放,而是根据滚动条的进度进行滚动。 这个属性有两个主要属性值:

  • animation-timeline: view()
    元素进入视口时开始播放动画,离开视口时结束。
  • animation-timeline: scroll()
    元素将在整个滚动容器中滚动时播放动画。
  • animation-timeline:cont-name
    命名容器,这个待会展开讲。
视口
view()
视口
scroll()

animation-timeline: scroll()

scroll()是对于整个页面的,相对而言比较简单,用于做一些与全局滚动相关的效果,例如文章阅读进度,这里引用前端侦探的一张图。 scroll scroll()中可以填入两个参数:scroller和axis

  • scroller
    scroller参数用于指定滚动容器,默认值为nearest
    如果设置为nearest,将会使用最近的祖先滚动容器。
    如果设置为root,将会使用文档视口作为滚动容器。
    如果设置为self,将会使用元素本身作为滚动容器。

  • axis
    axis参数用于指定滚动轴,默认值为block
    如果设置为block,滚动容器的块级轴方向。 如果设置为inline,滚动容器内联轴方向。 如果设置为x,则元素将在水平方向上滚动。
    如果设置为y,则元素将在垂直方向上滚动。

animation-range

如果我不想让动画整个滚动期间都播放呢,可以使用animation-range属性设置动画的起止位置,px%单位均可。
例如:

1
2
3
.animation{
    animation-range: 0 100px;
}

这样动画就只会在滚动容器滚动到0到100px的位置时播放,在100px之后就不会再播放了。

animation-timeline:view()

view()是相对于元素与视口的位置的,这个案例就是基于view()的。
view()中也可以填入两个参数:axios和inset

  • axis
    axis参数用于指定滚动轴,默认值为block
    如果设置为block,滚动容器的块级轴方向。 如果设置为inline,滚动容器内联轴方向。 如果设置为x,则元素将在水平方向上滚动。
    如果设置为y,则元素将在垂直方向上滚动。

  • inset
    inset参数用于规定动画从何时开始何时结束,是元素刚冒尖尖就开始还是元素完全进入视口呢,就靠这个属性控制,有点类似于上面animation-range的作用。
    inset接受一个或两个值,两个值时代表开始位置和结束位置,px%单位均可

animation-timeline:cont-name

你可能发现了,仅靠上面那些属性,只能实现父元素滚动驱动子元素的动画,要是我需要滚动一个容器去驱动另一个兄弟元素的动画呢?就需要靠命名的容器实现了。
使用很简单,就是在滚动容器上使用一个属性scroll-timeline-name

1
2
3
4
5
6
7
8
.scroll{
    /* 命名滚动容器 */
    scroll-timeline-name: --my-scroller;
}
.animation{
    /* 需要被驱动动画的元素 */
    animation-timeline: --my-scroller;
}

这样.scroll容器被滚动时,.animation元素就会根据滚动容器的滚动进度进行动画播放。

相关知识

position:fixed
animation-timeline
animation-range
前端侦探

Licensed under CC BY-NC-SA 4.0
最后更新于 Sep 14, 2025 00:00 UTC
使用 Hugo 构建
主题 StackJimmy 设计