手机刷网页卡成PPT,滚动动画怎么破?用CSS新特性实现高性能文本漩涡

2,403字
10–15 分钟
in

经常在网上冲浪,看到那种特别炫酷的滚动动画,比如文字随着手指滑动像漩涡一样转起来,心里就痒痒的。但有时候拿手机一刷,好家伙,直接卡成PPT,甚至直接黑屏罢工。那些用JS硬刚出来的效果,在性能面前多少有点力不从心。其实,现在CSS里藏着不少新宝贝,能把这种高难度的滚动动画从主线程的“重体力活”变成GPU加速的“丝滑小连招”。

目录

核心战力:sibling-index()

这玩意儿是干啥的

在CSS里,sibling-index() 就像给一群排队的小朋友发了个号码牌。它能精准知道当前元素在它所有兄弟元素里排第几位。这个功能加上它的好搭档 sibling-count(),相当于让CSS拥有了简单的“数数”能力,不用JS在背后计算,就能根据元素的位置来动态调整样式。

怎么给文字排兵布阵

要实现那个文字漩涡,第一步就得把一句话拆成一个一个独立的“兵”。这波操作虽然得请JS来帮忙拆开文本,但拆完之后的动画全交给CSS跑,性能稳得很。

<div class="vortex">
  <div class="char">这</div>
  <div class="char">是</div>
  <div class="char">一</div>
  <div class="char">个</div>
  <div class="char">字</div>
  <!-- 后面的字依次排列 -->
</div>

坑位预警:拆分的时候,空格最容易翻车。如果不对空格做特殊处理,它可能会消失不见,导致文字全粘在一起。通常的骚操作是把普通空格替换成一个“占位空格”,让插件也把它当成一个独立的字符盒子来处理。

从静态文字到动态漩涡

搭建螺旋舞台

所有字符的父容器 .vortex 是这场表演的大舞台。给它加上 position: fixedleft: 50% 是为了让整个漩涡始终固定在屏幕中间。重点是用 animation-timeline: scroll() 把动画和滚动进度绑定起来,滑多少,转多少。

性能玄学:父容器的动画只是整体的缩放和旋转,这种变换在GPU里跑得飞快,不会给主线程添堵。

给每个字符算算“座位”

每个字符的 --radius--rotation 变量是关键。借助 sibling-index(),第一个字符离中心最远,最后一个字符几乎贴到中心,同时每个字符的角度也都不一样,均匀分布在圆周上。这样一来,所有字符自然就摆成了一个螺旋形。

.vortex {
  position: fixed;
  left: 50%;
  height: 100vh;
  animation-timeline: scroll();

  .char {
    --radius: calc(10vh - (7vh/sibling-count() * sibling-index()));
    --rotation: calc((360deg * 3/sibling-count()) * sibling-index());

    position: absolute !important;
    top: 50%;
    left: 50%;
    transform: rotate(var(--rotation)) translateY(calc(-2.9 * var(--radius))) scale(calc(.4 - (.25/sibling-count() * sibling-index())));
    animation-timeline: scroll();
  }
}

翻车现场:公式里的系数,比如 10vh7vh 这些数值,得根据文字数量和屏幕大小反复调。如果直接照搬别人的数值,在小屏手机上看可能就成一坨浆糊。

让漩涡转起来,字一个一个亮

为了让效果更带感,除了位置螺旋,还可以让文字在滚动中逐渐显现。利用 animation-range-start 配合 sibling-index(),让每个字符的淡入时机错开。先出现的字符先亮,后出现的后亮,滚动时就有种一层层被吸进去的视觉冲击。

.char {
  animation-name: fade-in;
  animation-range-start: calc(90%/sibling-count() * sibling-index());
  animation-fill-mode: forwards;
  animation-timeline: scroll();
}

@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

节奏把控90% 这个基准值决定了动画的起始点。如果设得太高,可能都滑到底了字还没亮完;设得太低,字又亮得太早,失去了悬念感。

方案二:纯CSS的极简螺旋(不依赖拆分)

如果觉得把每个字拆开太麻烦,或者想快速实现一个类似的螺旋效果,可以直接用一个 <div> 包住所有文字,然后给这个容器做旋转和缩放。

<div class="simple-vortex">
  <p>这里是一段完整的文字,不需要拆开</p>
</div>
.simple-vortex {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  animation-timeline: scroll();
  animation-name: vortex-suck;
  animation-duration: auto;
  animation-fill-mode: forwards;
}

@keyframes vortex-suck {
  from {
    transform: translate(-50%, -50%) scale(1) rotate(0deg);
    opacity: 1;
  }
  to {
    transform: translate(-50%, -50%) scale(0) rotate(720deg);
    opacity: 0;
  }
}

适用场景:这种玩法适合做整体的转场效果,比如一段提示语或者品牌名,滚动时直接旋转缩小消失。虽然不如拆字那么酷炫,但胜在代码量少,兼容性也更好,手机上跑起来一点压力都没有。