CSS 时间控制:利用 `animation-delay` 的负值实现动画的时间跳跃

CSS 时间控制:利用 animation-delay 的负值实现动画的时间跳跃

大家好,今天我们来深入探讨一个 CSS 动画中不太常用,但却非常强大的技巧:利用 animation-delay 的负值来实现动画的时间跳跃效果。 掌握了这个技巧,你可以实现更复杂的动画控制,例如从动画的中间部分开始播放,或者创建一些有趣的视觉特效。

1. animation-delay 的基本概念

首先,让我们回顾一下 animation-delay 的基本用法。 animation-delay 属性指定动画开始播放前的延迟时间。 它的值可以是正数、负数或者零。

  • 正值: 动画会在指定的延迟时间后开始播放。 例如,animation-delay: 2s; 表示动画会在 2 秒后开始。

  • 零值: 动画会立即开始播放。 animation-delay: 0s; 或者不设置 animation-delay 属性,效果是一样的。

  • 负值: 这就是我们今天关注的重点。 当 animation-delay 为负数时,动画会立即开始播放,但会从动画的中间部分开始。 负值的绝对值决定了动画从哪个时间点开始播放。

2. 负值 animation-delay 的工作原理

animation-delay 为负数时,浏览器会假定动画已经运行了指定的延迟时间(负值的绝对值),然后从该时间点开始播放。 换句话说,动画会跳过从 0 到 |animation-delay| 的这段时间。

举个例子,如果动画的总时长为 5 秒,并且设置 animation-delay: -2s;,那么动画会立即开始播放,但会跳过动画的前 2 秒。 实际上,动画是从第 3 秒的位置开始播放的。

3. 代码示例:从动画中间开始播放

让我们通过一个简单的例子来演示如何使用负值 animation-delay。 假设我们有一个让元素从左向右移动的动画:

<!DOCTYPE html>
<html>
<head>
<style>
.box {
  width: 100px;
  height: 100px;
  background-color: red;
  position: relative;
  animation-name: move;
  animation-duration: 5s;
  animation-iteration-count: infinite; /* 循环播放 */
  animation-timing-function: linear; /* 线性速度 */
}

@keyframes move {
  0% {
    left: 0;
  }
  100% {
    left: 500px;
  }
}

/* 从动画中间开始播放 */
.box.delayed {
  animation-delay: -2.5s; /* 动画总时长 5 秒,-2.5s 表示从中间开始 */
}
</style>
</head>
<body>

<div class="box"></div>
<div class="box delayed"></div>

</body>
</html>

在这个例子中,我们定义了一个名为 move 的关键帧动画,它让 .box 元素从左向右移动 500 像素,总时长为 5 秒。

我们创建了两个 .box 元素。 第一个元素没有设置 animation-delay,所以它会从动画的开始处播放。 第二个元素设置了 animation-delay: -2.5s;,这意味着它会从动画的中间位置(2.5 秒处)开始播放。

运行这段代码,你会看到第二个 .box 元素立即从中间位置开始移动,而第一个 .box 元素则从起点开始移动。

4. 实际应用场景

负值 animation-delay 在很多场景下都非常有用。 这里列举一些常见的应用场景:

  • 创建循环动画的平滑过渡: 在循环动画中,有时我们需要让动画在循环的开始和结束之间平滑过渡。 通过使用负值 animation-delay,我们可以调整动画的起始位置,从而实现平滑的过渡效果。

  • 模拟倒带效果: 通过动态改变 animation-delay 的负值,我们可以模拟动画的倒带效果。

  • 响应用户交互: 当用户与页面交互时,我们可以根据用户的操作来改变 animation-delay 的值,从而控制动画的播放进度。例如,当用户鼠标悬停在一个元素上时,我们可以让动画从悬停的位置开始播放。

  • 实现复杂的动画序列: 在复杂的动画序列中,我们可以使用负值 animation-delay 来精确控制每个动画的开始时间,从而创建更精细的动画效果。

5. 代码示例:鼠标悬停时从特定位置开始动画

以下示例演示了如何使用 JavaScript 和 CSS 来实现鼠标悬停时从特定位置开始动画的效果:

<!DOCTYPE html>
<html>
<head>
<style>
.box {
  width: 100px;
  height: 100px;
  background-color: blue;
  position: relative;
  animation-name: rotate;
  animation-duration: 4s;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
}

@keyframes rotate {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
</style>
</head>
<body>

<div class="box" id="myBox"></div>

<script>
const box = document.getElementById('myBox');

box.addEventListener('mouseover', function() {
  // 停止之前的动画
  box.style.animationPlayState = 'paused';

  // 计算动画的当前时间
  const computedStyle = window.getComputedStyle(box);
  const animationDuration = parseFloat(computedStyle.animationDuration);
  const animationDelay = parseFloat(computedStyle.animationDelay) || 0; // 处理未设置 animation-delay 的情况
  const elapsedTime = (performance.now() / 1000) % animationDuration; // 考虑到 performance.now() 单位是毫秒

  // 设置新的 animation-delay,从当前位置开始播放
  box.style.animationDelay = `-${elapsedTime}s`;

  // 重新启动动画
  box.style.animationPlayState = 'running';
});

box.addEventListener('mouseout', function() {
    // 停止动画
    box.style.animationPlayState = 'paused';

    // 重置 animation-delay 为初始状态 (可选)
    box.style.animationDelay = '0s';

    //重新开始动画,保证动画从头开始,而不是暂停的地方
    box.style.animationPlayState = 'running';
});
</script>

</body>
</html>

在这个例子中,我们创建了一个让 .box 元素旋转的动画。 当鼠标悬停在 .box 元素上时,我们会停止动画,计算动画的当前时间,并使用负值 animation-delay 将动画设置为从当前位置开始播放。 当鼠标移开时,动画会停止,并重置 animation-delay,使动画从头播放。

代码解释:

  1. 获取元素和添加事件监听器: 获取 ID 为 myBox 的元素,并添加 mouseovermouseout 事件监听器。

  2. mouseover 事件处理:

    • box.style.animationPlayState = 'paused';: 暂停动画。
    • window.getComputedStyle(box): 获取元素的计算样式,包括动画属性。
    • parseFloat(computedStyle.animationDuration): 获取动画的持续时间(例如,"4s" 转换为 4)。
    • parseFloat(computedStyle.animationDelay) || 0: 获取动画的初始延迟。如果未设置延迟,则默认为 0。这处理了初始状态下没有设置 animation-delay 的情况。
    • const elapsedTime = (performance.now() / 1000) % animationDuration;: 计算动画的当前时间。 performance.now() 提供高精度的时间戳,单位是毫秒。 我们将它除以 1000 转换为秒,然后使用模运算符 % 获取当前时间在动画周期内的位置。
    • box.style.animationDelay =-${elapsedTime}s`: 设置新的animation-delay` 为负的当前时间。 这使得动画从当前位置开始播放。
    • box.style.animationPlayState = 'running';: 重新启动动画。
  3. mouseout 事件处理:

    • box.style.animationPlayState = 'paused';: 暂停动画。
    • box.style.animationDelay = '0s';: 重置 animation-delay 为 0,使得动画下次从头开始播放。
    • box.style.animationPlayState = 'running';: 重新启动动画。

6. animation-play-state 的作用

在上面的例子中,我们使用了 animation-play-state 属性来暂停和重新启动动画。 animation-play-state 属性控制动画的播放状态。 它可以有两个值:

  • running: 动画正在播放。
  • paused: 动画已暂停。

通过设置 animation-play-state 的值为 paused,我们可以停止动画的播放,并保持动画的当前状态。 然后,我们可以通过设置 animation-play-state 的值为 running 来重新启动动画。

7. 注意事项

在使用负值 animation-delay 时,需要注意以下几点:

  • 动画必须已经定义: animation-delay 的负值只有在动画已经定义的情况下才有效。 如果没有定义动画,设置 animation-delay 不会产生任何效果。
  • 动画时长是关键: 负值的绝对值不能大于动画的总时长。 如果负值的绝对值大于动画的总时长,动画会从动画的结尾处开始播放。
  • 浏览器兼容性: 负值 animation-delay 在现代浏览器中都有很好的支持。 但是,在一些旧版本的浏览器中,可能存在兼容性问题。
  • 性能问题: 过度使用负值 animation-delay 可能会导致性能问题,特别是在复杂的动画中。 因此,应该谨慎使用,并进行性能测试。
  • 与 JavaScript 结合使用: 通常情况下,负值 animation-delay 会与 JavaScript 结合使用,以实现更灵活的动画控制。

8. 更复杂的例子:模拟加载条

我们可以使用负值 animation-delay 创建一个模拟加载条的效果,让加载条在加载完成之前就显示一部分已加载的内容。

<!DOCTYPE html>
<html>
<head>
<style>
.loading-bar {
  width: 200px;
  height: 10px;
  background-color: #eee;
  position: relative;
  overflow: hidden; /* 隐藏超出部分 */
}

.loading-bar-inner {
  width: 100%;
  height: 100%;
  background-color: green;
  position: absolute;
  left: -100%; /* 初始位置在左侧隐藏 */
  animation: load 5s linear forwards; /* 5秒完成加载 */
}

@keyframes load {
  0% {
    left: -100%;
  }
  100% {
    left: 0;
  }
}

/* 模拟加载 50% 的效果 */
.loading-bar.loaded .loading-bar-inner {
  animation-delay: -2.5s; /* -5s * 0.5 = -2.5s */
}
</style>
</head>
<body>

<div class="loading-bar">
  <div class="loading-bar-inner"></div>
</div>

<div class="loading-bar loaded">
  <div class="loading-bar-inner"></div>
</div>

</body>
</html>

在这个例子中,我们创建了一个 .loading-bar 元素,它包含一个 .loading-bar-inner 元素。 .loading-bar-inner 元素通过 load 动画从左向右移动,模拟加载过程。

我们创建了两个 .loading-bar 元素。 第一个元素显示完整的加载过程。 第二个元素添加了 .loaded 类,并设置 animation-delay: -2.5s;,这意味着它会从动画的中间位置开始播放,模拟加载了 50% 的效果。

代码解释:

  • .loading-bar: 外层容器,设置宽度、高度和背景颜色,并使用 overflow: hidden; 隐藏 .loading-bar-inner 超出部分。
  • .loading-bar-inner: 内层元素,表示加载进度。 初始位置设置为 left: -100%;,使其隐藏在左侧。 animation: load 5s linear forwards; 定义了动画,forwards 确保动画结束后停留在 100% 状态。
  • @keyframes load: 定义了 load 动画,从 left: -100%; 移动到 left: 0;
  • .loading-bar.loaded .loading-bar-inner: 关键部分。 .loading-bar.loaded 表示添加了 loaded 类的 .loading-bar 元素。 animation-delay: -2.5s; 设置了负延迟,使得加载条从 50% 的位置开始。 -2.5s 是总动画时间 (5s) 的一半。

9. 表格总结 animation-delay 的用法

animation-delay 动画播放行为
正数 (例如, 2s) 动画会在指定的延迟时间后开始播放。
零 (例如, 0s) 动画会立即开始播放。
负数 (例如, -2s) 动画会立即开始播放,但会从动画的中间部分开始。 负值的绝对值决定了动画从哪个时间点开始播放。 负值的绝对值不能大于动画的总时长,否则,动画将从结束位置开始。

10. 巧妙利用时间跳跃,动画控制更灵活

animation-delay 的负值提供了一种强大的方式来控制 CSS 动画的播放时间。 通过合理地使用这个技巧,我们可以实现更复杂的动画效果,并为用户提供更好的交互体验。 理解并灵活运用负值 animation-delay,可以让你在 CSS 动画的世界里更加游刃有余。记住,结合 JavaScript 可以实现更动态和交互性更强的动画效果。

更多IT精英技术系列讲座,到智猿学院

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注