跳转到内容

利用延迟实现复杂动画

在某些场景中需要将动画效果的进度使用 js 控制,如果是简单的动画,自是不难,但比较复杂的动画效果,使用 js 控制进度就比较麻烦了。比如下面的例子。

BOX
css
@keyframes move {
  0% {
    transform: translate(-100px, 0) rotate(0deg) scale(1);
  }
  50% {
    transform: translate(0px, -30px) rotate(180deg) scale(1.5);
  }
  100% {
    transform: translate(100px, 0) rotate(360deg) scale(1);
  }
}

.box {
  /* 加上 paused 使动画暂停 */
  animation: move linear forwards 1s paused;

  /* 
      这时候设置动画延迟为 -0.5s,是不是代表了动画已经进行了 0.5s,
      那使用 js 控制动画延迟,不就控制了动画进度嘛。
    */
  animation-delay: -0.5s;
}

如果有多个动画需要同时进度,可以将一个 css 变量,放在它们共同的父元素上,然后使用 js 控制这个 css 变量,这样就可以控制多个动画的进度了。

css
.box {
  --delay: 0;
}
.box .animation1 {
  animation: move1 linear forwards 1s paused;
  animation-delay: var(--delay);
}
.box .animation2 {
  animation: move2 linear forwards 1s paused;
  animation-delay: var(--delay);
}
.box .animation3 {
  animation: move3 linear forwards 1s paused;
  animation-delay: var(--delay);
}
js
// 使用 js 控制 --delay 的值
boxDom.style.setProperty('--delay', `-${input.value}s`)

上面 demo 的代码如下:

Details
vue
<template>
  <demo>
    <div class="container">
      <div class="animation-box flex-center">
        <div class="box flex-center" ref="boxRef">BOX</div>
      </div>
      <div class="range-box flex-center">
        <input
          type="range"
          min="0"
          max="1"
          step="0.01"
          value="0.5"
          ref="rangeRef"
          @input="setDelay"
        />
      </div>
    </div>
  </demo>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'

const boxRef = ref<HTMLDivElement>()
const rangeRef = ref<HTMLInputElement>()

const setDelay = () => {
  boxRef.value?.style.setProperty('--delay', `-${rangeRef.value?.value}s`)
}

onMounted(setDelay)
</script>

<style scoped>
.container {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.animation-box {
  margin-top: 20px;
  height: 100px;
}
.box {
  --delay: 0;
  background-color: plum;
  color: #fff;
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 50px;
  animation: move linear forwards 1s paused;
  animation-delay: var(--delay);
}
.range-box {
  margin-top: 1rem;
}

@keyframes move {
  0% {
    transform: translate(-100px, 0) rotate(0deg) scale(1);
  }
  50% {
    transform: translate(0px, -20px) rotate(180deg) scale(1.5);
  }
  100% {
    transform: translate(100px, 0) rotate(360deg) scale(1);
  }
}
</style>

基于这个原理,可以将一些比较复杂的动画效果,通过 js 与滚动、加载进度等结合,实现一些比较炫酷的效果。