CSS `Web Animations API` (`WAAPI`) `KeyframeEffect` 与 `AnimationTimeline`

各位观众老爷,大家好!今天咱们来聊聊前端动画界的一对新秀——CSS Web Animations API(WAAPI)中的KeyframeEffectAnimationTimeline。别怕,听名字挺吓人,其实它们能让你用JavaScript控制CSS动画,玩出更多花样,而且比直接操作CSS类名、过渡啥的更强大、更灵活。

WAAPI:动画界的瑞士军刀

首先,咱得明确一下WAAPI是个啥。简单来说,它是一套JavaScript API,允许你通过代码创建、控制和操作Web动画。你可以把它想象成一把瑞士军刀,各种动画需求都能用它来解决。而KeyframeEffectAnimationTimeline就是这把军刀上的两把常用刀具。

KeyframeEffect:动画的剧本

KeyframeEffect就像动画的剧本,它描述了动画在不同时间点的状态。你可以在剧本里指定元素在什么时候应该是什么样子,比如位置、大小、颜色等等。

语法:

new KeyframeEffect(
  element, // 要应用动画的元素
  keyframes, // 关键帧数组或关键帧对象
  options // 动画选项
);
  • element: 动画应用的目标元素,HTMLElement 对象。
  • keyframes: 一个数组或对象,定义动画的关键帧。
    • 数组形式:每个元素都是一个对象,包含CSS属性和对应的值,表示在该时间点的状态。
    • 对象形式:以CSS属性为键,值为包含值的数组,数组的每个元素对应一个关键帧的值。
  • options: 一个对象,定义动画的选项,例如:
    • duration: 动画持续时间,单位毫秒。
    • easing: 缓动函数,例如linear, ease, ease-in, ease-out, ease-in-out
    • delay: 动画延迟开始的时间,单位毫秒。
    • iterations: 动画重复次数,Infinity表示无限重复。
    • direction: 动画播放方向,例如normal, reverse, alternate, alternate-reverse
    • fill: 动画在开始和结束时的填充模式,例如none, forwards, backwards, both

举个栗子:

<div id="box" style="width: 100px; height: 100px; background-color: red; position: relative;"></div>
<script>
  const box = document.getElementById('box');

  // 关键帧数组
  const keyframes = [
    { transform: 'translateX(0)' },
    { transform: 'translateX(200px)' },
    { transform: 'translateX(0)' }
  ];

  // 动画选项
  const options = {
    duration: 2000, // 2秒
    easing: 'ease-in-out', // 缓动函数
    iterations: Infinity // 无限循环
  };

  // 创建 KeyframeEffect
  const keyframeEffect = new KeyframeEffect(box, keyframes, options);

  // 创建 Animation 对象 (后面会讲)
  const animation = new Animation(keyframeEffect, document.timeline);

  // 播放动画
  animation.play();
</script>

这个例子创建了一个红色的方块,它会在水平方向上来回移动,持续2秒,无限循环,并且使用了缓动函数。

AnimationTimeline:动画的时间线

AnimationTimeline就像电影的时间轴,它管理着动画的播放进度。默认情况下,浏览器提供了一个文档级的AnimationTimeline,可以通过document.timeline访问。 你也可以创建自定义的AnimationTimeline,但这种情况比较少见。

document.timeline 提供了当前文档的全局动画时间。它主要用于创建和管理动画。

  • currentTime: 当前动画的时间,以毫秒为单位。
  • getAnimations(): 返回一个包含所有与此时间线关联的 Animation 对象的数组。
// 获取当前时间
const currentTime = document.timeline.currentTime;
console.log(currentTime);

// 获取所有与时间线关联的动画
const animations = document.timeline.getAnimations();
console.log(animations);

Animation:把剧本搬上舞台

有了剧本(KeyframeEffect)和时间线(AnimationTimeline),还需要一个导演把剧本搬上舞台,这个导演就是Animation对象。Animation对象负责将KeyframeEffect应用到指定的元素上,并按照时间线的进度来控制动画的播放。

语法:

new Animation(
  effect, // KeyframeEffect 对象
  timeline // AnimationTimeline 对象
);
  • effect: 要播放的 KeyframeEffect 对象。
  • timeline: AnimationTimeline 对象,指定动画的时间线。如果省略,则使用 document.timeline

Animation 对象提供了一些方法来控制动画的播放:

  • play(): 开始或恢复播放动画。
  • pause(): 暂停动画。
  • reverse(): 反向播放动画。
  • cancel(): 取消动画,将元素恢复到动画开始前的状态。
  • finish(): 立即将动画跳转到结束状态。
  • updatePlaybackRate(playbackRate): 设置动画的播放速度,playbackRate 为 1 表示正常速度,0.5 表示慢速,2 表示快速,负数表示反向播放。
  • currentTime: 获取或设置动画的当前时间。
  • playbackRate: 获取或设置动画的播放速度。
  • onfinish: 动画完成时触发的事件。
  • oncancel: 动画取消时触发的事件。

把之前的例子补全:

<div id="box" style="width: 100px; height: 100px; background-color: red; position: relative;"></div>
<script>
  const box = document.getElementById('box');

  // 关键帧数组
  const keyframes = [
    { transform: 'translateX(0)' },
    { transform: 'translateX(200px)' },
    { transform: 'translateX(0)' }
  ];

  // 动画选项
  const options = {
    duration: 2000, // 2秒
    easing: 'ease-in-out', // 缓动函数
    iterations: Infinity // 无限循环
  };

  // 创建 KeyframeEffect
  const keyframeEffect = new KeyframeEffect(box, keyframes, options);

  // 创建 Animation 对象
  const animation = new Animation(keyframeEffect, document.timeline);

  // 播放动画
  animation.play();

  // 暂停动画(5秒后)
  setTimeout(() => {
    animation.pause();
    console.log("动画暂停");
    // 恢复动画(2秒后)
    setTimeout(() => {
      animation.play();
      console.log("动画恢复");
    }, 2000);
  }, 5000);

  // 动画完成时触发的事件
  animation.onfinish = () => {
    console.log("动画完成");
  };
</script>

这个例子演示了如何使用 play(), pause(), 和 onfinish 事件。

KeyframeEffectAnimationTimeline 的关系

简单来说,KeyframeEffect 定义了动画的内容,AnimationTimeline 定义了动画的时间轴,而 Animation 对象将两者结合起来,控制动画的播放。KeyframeEffect 描述了“动画应该怎么动”,AnimationTimeline 描述了“动画在什么时候动”,Animation 对象则负责将两者连接起来,让动画真正跑起来。

更高级的用法:自定义 AnimationTimeline

虽然默认的 document.timeline 已经足够用了,但在某些特殊情况下,你可能需要自定义 AnimationTimeline。例如,你想让动画的播放速度与用户的滚动距离相关联,这时就需要自己创建一个时间线,并根据滚动距离来更新时间线的 currentTime

创建自定义的时间轴 (Experimental):

const myTimeline = new AnimationTimeline();

实际应用场景

  • 滚动动画(Scroll-Driven Animations): 根据页面滚动的位置来控制动画的进度,实现视差滚动效果或其他复杂的滚动交互。
  • 手势动画(Gesture-Driven Animations): 根据用户的手势操作来控制动画的播放,例如拖拽、滑动等。
  • 复杂的动画编排: 使用 WAAPI 可以更灵活地编排多个动画,实现更复杂的动画效果。
  • 状态驱动的动画: 根据应用的状态来触发不同的动画,例如加载动画、过渡动画等。

案例:滚动驱动的进度条

<!DOCTYPE html>
<html>
<head>
  <title>滚动驱动的进度条</title>
  <style>
    body {
      height: 2000px; /* 模拟长页面 */
    }
    #progress-bar {
      position: fixed;
      top: 0;
      left: 0;
      width: 0%;
      height: 5px;
      background-color: blue;
      z-index: 1000;
    }
  </style>
</head>
<body>
  <div id="progress-bar"></div>

  <script>
    const progressBar = document.getElementById('progress-bar');

    // 获取文档的总高度
    const totalHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight;

    // 定义关键帧
    const keyframes = [
      { width: '0%' },
      { width: '100%' }
    ];

    // 定义动画选项
    const options = {
      duration: 1, // duration 不重要,因为我们会手动控制
      fill: 'forwards'
    };

    // 创建 KeyframeEffect
    const keyframeEffect = new KeyframeEffect(progressBar, keyframes, options);

    // 创建 Animation 对象
    const animation = new Animation(keyframeEffect, document.timeline);

    // 手动暂停动画
    animation.pause();

    // 监听滚动事件
    window.addEventListener('scroll', () => {
      // 计算滚动百分比
      const scrollPercentage = (document.documentElement.scrollTop / totalHeight) * 100;

      // 设置动画的当前时间,注意单位是秒
      animation.currentTime = (scrollPercentage / 100);
    });
  </script>
</body>
</html>

这个例子创建了一个固定在页面顶部的蓝色进度条,当页面滚动时,进度条的宽度会根据滚动距离来更新,从而显示当前的滚动进度。 关键在于监听 scroll 事件,并根据滚动的百分比来更新 animation.currentTime。由于我们要手动控制动画,所以duration设置成1即可。

总结与建议

特性 描述 优点 缺点
KeyframeEffect 定义动画的关键帧,描述动画在不同时间点的状态。 灵活地定义动画的各个阶段,支持各种CSS属性,方便创建复杂的动画效果。 语法相对繁琐,需要手动编写关键帧,对于简单的动画可能略显复杂。
AnimationTimeline 管理动画的播放进度,提供全局动画时间。 提供了统一的时间轴,方便管理和同步多个动画,支持自定义时间轴,可以实现更高级的动画效果。 对于简单的动画来说,使用默认的时间轴就足够了,自定义时间轴的使用场景相对较少。
WAAPI 一套JavaScript API,允许你通过代码创建、控制和操作Web动画。 提供了强大的动画控制能力,可以实现各种复杂的动画效果,性能优于传统的CSS动画,方便进行动画的编排和管理。 学习曲线相对较陡峭,需要理解WAAPI的各个概念和API,对于简单的动画可能略显复杂。
  • 多实践: WAAPI 的概念和 API 比较多,需要多写代码才能真正掌握。
  • 善用工具: 可以使用一些 WAAPI 的辅助工具,例如在线编辑器、动画库等,来提高开发效率。
  • 关注性能: 复杂的动画可能会影响页面的性能,需要注意优化动画效果,避免过度使用 WAAPI。
  • 逐步深入: 先从简单的动画开始,逐步尝试更复杂的动画效果,循序渐进地学习 WAAPI。
  • 兼容性: 虽然 WAAPI 的兼容性已经比较好了,但在一些老版本的浏览器上可能需要使用 Polyfill。

总的来说,KeyframeEffectAnimationTimeline 是 WAAPI 中非常重要的两个概念,它们为我们提供了更强大、更灵活的动画控制能力。 掌握了它们,你就可以用 JavaScript 写出各种炫酷的 Web 动画,让你的网站更加生动有趣。 好了,今天的讲座就到这里,希望对大家有所帮助! 咱们下期再见!

发表回复

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