搞不懂CSS滚动动画?,看完这波操作让动画跟着滚动条走

2,423字
10–15 分钟
in

网页上那些随着滚动条滚啊滚,图片突然变大变清晰的效果,是不是看着特别酷?以前要实现这玩意儿,可能得请出JavaScript大佬,还得搬出Intersection Observer才能搞定。现在不一样了,CSS亲自动手,直接给安排了个叫animation-timeline的狠角色。这货带上view()函数,就能让动画不再依赖时间,而是看元素在滚动窗口里露了多少脸。简单说,就是元素滚到哪儿,动画就跑到哪儿,妥妥的跟屁虫。

目录

先整个架子

要做个能滚动的小玩意儿,最基本的得有个装东西的盒子。就拿常见的那种横向滚动图集来说,整个大盒子当容器,里面塞上一排排的图片卡片就行。

<main class="rolling-box">
  <div class="roll-item">
    <!-- 开头可以放个空的占位 -->
  </div>
  <div class="roll-item">
      <img src="pic-1.jpg" alt="风景照1">
  </div>
  <div class="roll-item">
      <img src="pic-2.jpg" alt="风景照2">
  </div>
  <!-- 中间多来几张 -->
  <div class="roll-item">
    <!-- 结尾也放个空的,让滚动更丝滑 -->
  </div>
</main>

盒子搭好了,得给它点样式。让它里面的东西排成一排,还得能左右划拉。

.rolling-box {
  display: flex;
  width: max(480px, 50vw);
  overflow-x: auto;
}

.rolling-box .roll-item {
  flex-shrink: 0;
  width: calc(100% / 3); /* 一次露三张,刚刚好 */
  aspect-ratio: .8;
  img {
    width: 100%;
  }
}

这样一弄,一个可以左右滑动的小玩意儿就成型了。为了让滑动更有手感,可以加点吸铁石一样的滑动吸附效果。

.rolling-box {
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
}

.roll-item {
  scroll-snap-align: center;
}

这步做完,滑动的时候每一张图都会稳稳地停在中间,手感立马就上来了。在这里需要注意的是,scrollbar-width: none只是把滚动条藏起来,不是让它消失,如果用户用的鼠标滚轮,还是能正常滚动的。

动画整起来

动画这玩意儿,以前怎么写,现在还怎么写。就是弄个关键帧,定义好从哪儿开始,到哪儿结束。咱们想要的效果是,在边上的图模糊又小,滚到中间就变得又大又清晰。

@keyframes focusIn {
  45%, 100% {
    transform: scale(0.6);
    border-radius: 20px;
    filter: blur(5px) brightness(.7);
  }
  50% {
    transform: scale(1);
    border-radius: 5px;
    filter: none;
  }
}

这个关键帧看着眼熟吧?跟平常写动画一模一样。接下来就是把动画绑到每个图片卡片上,但是跟以前不一样,这次不按时间线走。

.roll-item {
  animation: focusIn;
  animation-timeline: view(inline);
}

就这一句animation-timeline: view(inline),直接把动画的节奏从“时间到了”换成了“滚动到了”。现在试试,每划动一下,图片的变化都跟着它在滚动窗口里的位置走,这效果简直yyds。有一点千万得记住,写animation-timeline的时候,最好放在animation简写属性的后面,不然可能会被默认的auto给覆盖掉,到时候动画就不跟着滚动了。

进阶玩法

光会用view()还不够,括号里其实还能塞点东西,让它更听话。view()可以接受两个参数,一个是方向(blockinlinexy),一个是偏移量(inset)。这个偏移量可太实用了,它决定了动画什么时候开始触发。

比如说,想让图片刚露出一丢丢就开始变,完全离开窗口才结束,就可以调整这个偏移量。下面这段代码就能让动画在元素进入窗口时就启动,而不是等它完全跑出去。

.roll-item {
  animation: focusIn linear both;
  animation-timeline: view(100% 0%);
}

这样一来,动画的触发时机就精准多了,不会出现那种一张图都滚到中间了才开始变化的尴尬场面。另外,还有个叫animation-range的属性也能干类似的事儿,比如animation-range: entry,意思就是元素进入滚动窗口的范围时触发动画,这两个属性配合着用,能把动画触发点拿捏得死死的。

再加点料

为了让轮播图不那么单调,还能玩点花的。比如给背景位置加点动画,让背景自己溜达,看起来像在平移一样。

动画属性效果描述
背景位置缓慢移动
缩放比例近大远小
模糊程度由虚变实

把这些效果组合一下,就能做出那种背景跟随鼠标或者滚动慢慢移动的感觉,视觉冲击力直接拉满。

@keyframes moveBg {
  0% {
    background-position: 0% 50%;
    filter: blur(4px);
  }
  100% {
    background-position: 100% 50%;
    filter: blur(0px);
  }
}

.roll-item {
  background-image: url('cool-bg.jpg');
  background-size: cover;
  animation: moveBg linear both;
  animation-timeline: view();
}

看到没,只要想象力够丰富,view()能玩出的花样多到数不清。从简单的图片缩放,到复杂背景移动,甚至还能结合@property去搞渐变角度的变化,把以前那些得靠JS才能做的交互,现在全交给CSS搞定。