← CSS Animation · 能力速览 / 控制无限动画:不跳变变速、平滑启停 待审核 17 / 22
infinite · 平滑变速 / 启停

控制无限动画:不跳变变速、平滑启停

无限动画(infinite)写起来容易,控制得平滑却不简单:悬停加速时直接改 animation-duration 会让元素瞬间跳位animation-play-state: paused 停下时则是急停,很生硬。本页介绍 Temani Afif 的两个纯 CSS 技巧,分别解决变速与启停;第一个技巧用到核心属性页讲过的 animation-composition。出处:Part 1Part 2

1 · 变速不跳变:animation-composition: add

思路:让同一个动画跑两层——第一层一直转,第二层默认 paused;再用 animation-composition: add 把两层的转动叠加。需要加速时,只把第二层的 play-state 设为 running:它从「当前贡献 0」开始往上加,所以位置完全不跳,转速却平滑变成两层之和。

@keyframes spin { to { rotate: 1turn; } }
.box {
  animation: spin var(--d)  linear infinite,
             spin var(--d2) linear infinite;
  animation-composition: add;
  animation-play-state: running, paused;
}
.box:hover { animation-play-state: running, running; }

关键在最后一行:两层都 running 时转速相加,但第二层的角度从 0 开始累加,所以启用它的瞬间画面不跳变。下面拖动倍率按钮,读数里的累计角度会连续增长、没有断点。

速度关系:启用第二层后角速度 = 1/d + 1/d2,取 d2=d/(s1)d2 = d/(s-1) 就得到 s 倍速。用 abs() / sign()--s 推广到减速 / 停住 / 反向(任意正负值)只需一个变量——完整通用公式见原文 Part 1(用到较新的 CSS 数学函数)。

2 · 平滑启停:用 transition 当「刹车」

play-state: paused/running 是开关式的急起急停。让它渐入渐出的办法:无限动画照常驱动一个属性(这里是 rotate),同时在另一个属性(transform)上挂一段 transition 做反向「刹车」。启动时反向量缓缓归零 → 像慢慢加速;停止时动画冻结、刹车量缓缓回弹 → 像滑行到停。

.box {
  animation: spin 2s linear infinite; animation-play-state: paused;
  transition: transform 1s ease-out;  transform: rotate(0);
}
.box.go {
  animation-play-state: running;
  transform: rotate(-.25turn);
}

思路是把「速度的突变」用一段反向 transition 抵消掉。原文 Part 2 还给了更通用的写法:在注册的 @property 角度上做加法(rotatecalc(var(--a) + var(--b))--btransition),以及用 @starting-style + mod() / sign() 让元素停时按最短路径回到初始角度——详见原文 Part 2

这些技巧用到 animation-composition 以及 abs() / sign() / mod()、注册 @property 等较新能力,以浏览器实现为准;本页两个 demo 只用到其中已较好支持的部分。