CSS 动画的暂停和恢复

114 阅读2分钟

初始实现

想要实现一个类似网易云音乐光碟转动的效果,第一感觉很简单,很快实现如下:

<div
  ref="playerDisc"
  :class="[
    'player-disc absolute inset-0 bg-contain bg-no-repeat',
    {
      'animation-play': !isPlaying,
    },
  ]"
></div>
<button
  type="button"
  ref="playerBtn"
  :class="[
    'player-btn absolute bg-transparent bg-contain bg-center bg-no-repeat',
    isPlaying ? 'play' : 'pause',
  ]"
  @click="togglePlay"
>
  <span v-if="isPlaying" class="sr-only">暂停</span>
  <span v-else class="sr-only">播放</span>
</button>


@keyframes spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
.animation-play {
  animation: spin 20s linear infinite;
}

CSS 实现很简单。

ChatGPT 实现

在实现暂停、播放功能时,突然发现,暂停之后,光碟 playerDisc 就恢复到了初始位置,那么如何在暂停时,让光碟保留在当前动画的位置呢?

ChatGPT 给出了以下方法:

playerBtn.value?.addEventListener('click', () => {
  if (playerDisc.value) {
    if (isPlaying.value) {
      const computedStyle = window.getComputedStyle(playerDisc.value)
      const matrix = computedStyle.transform
      if (matrix !== 'none') {
        const values = matrix.split('(')[1].split(')')[0].split(',')
        const a = Number(values[0])
        const b = Number(values[1])
        currentRotation.value = Math.round(Math.atan2(b, a) * (180 / Math.PI))
        playerDisc.value.style.transform = `rotate(${currentRotation.value}deg)`
      }
    } else {
      playerDisc.value.style.transform = `rotate(${currentRotation.value}deg)`
    }
    isPlaying.value = !isPlaying.value
  }
})

通过 js 获取元素的样式,获取旋转角度。这样在暂停的时候,确实可以保持在当前位置,但是恢复播放时又从初始角度开始了。

继续问 ChatGPT,给出了更复杂的、用 js requestAnimationFrame 实现动画的方案,而不是 CSS 动画。

animation-play-state

简单搜索了一下,原来 CSS 动画还支持这么多属性,其中 animation-play-state 就是设置动画的运动状态:

  • running:当前动画正在运行

  • paused:当前动画已被停止

ChatGPT 其实一开始就给出了方法:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CSS动画暂停</title>
    <style>
        .box {
            width: 100px;
            height: 100px;
            background-color: blue;
            position: relative;
            animation: move 4s linear infinite;
            animation-play-state: paused; /* 默认暂停 */
            animation-fill-mode: forwards; /* 保持动画最后一帧状态 */
        }

        @keyframes move {
            0% { left: 0; }
            100% { left: 300px; }
        }
    </style>
</head>
<body>
    <div class="box"></div>
    <button onclick="toggleAnimation()">切换动画状态</button>

    <script>
        function toggleAnimation() {
            const box = document.querySelector('.box');
            if (box.style.animationPlayState === 'paused') {
                box.style.animationPlayState = 'running';
            } else {
                box.style.animationPlayState = 'paused';
            }
        }
    </script>
</body>
</html>

只是我错误的运用了,在我给出自己的代码继续让它修改时,它不坚定的给出了复杂的东西。

.animation-play {
  animation: spin 20s linear infinite;
  animation-fill-mode: forwards;
  animation-play-state: running;
}
.animation-pause {
  animation-play-state: paused;
}

我在播放时应用 .animation-play,在暂停时应用 .animation-pause,但是 .animation-pause 中并没有动画名称。

暂停时,.animation-play .animation-pause 叠加使用就好了,或者 .animation-pause 中写上动画的其他属性。

这样 CSS 动画就轻松的实现暂停和恢复功能。

总结

  • 很小的一个问题,记录一下,希望对遇到的人有帮助。
  • 主要还是 CSS 基本功问题,(CSS 新内容其实很多)。
  • 遇到问题,不要把它复杂化,多想想是不是方法的问题。
  • 看文档一定要去理解,才能正确使用。不要忙碌的尝试就放弃。