MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

JavaScript利用CSS实现动画效果

2023-06-206.4k 阅读

一、CSS 动画基础

在深入探讨如何使用 JavaScript 利用 CSS 实现动画效果之前,我们先来回顾一下 CSS 动画的基础知识。CSS 动画主要有两种方式:过渡(transition)和关键帧动画(@keyframes)。

1.1 过渡(transition

过渡允许在元素的属性值从一个状态平滑地过渡到另一个状态时创建动画效果。它通过指定过渡的属性、持续时间、过渡的时间函数以及延迟时间来定义动画。

语法

transition: property duration timing - function delay;
  • property:指定要过渡的 CSS 属性,如 widthheightopacity 等。如果要指定多个属性,可以用逗号分隔。如果使用 all,则表示所有可动画化的属性都将过渡。
  • duration:指定过渡效果持续的时间,单位通常为秒(s)或毫秒(ms)。
  • timing - function:定义过渡的速度曲线。常见的值有:
    • ease:默认值,开始和结束时较慢,中间较快。
    • linear:匀速过渡。
    • ease - in:开始时较慢。
    • ease - out:结束时较慢。
    • ease - in - out:开始和结束时都较慢。
    • cubic - bezier(n,n,n,n):通过贝塞尔曲线自定义速度曲线。
  • delay:指定过渡效果开始前的延迟时间,单位同样为秒(s)或毫秒(ms)。

示例

<!DOCTYPE html>
<html lang="en">

<head>
  <style>
    .box {
      width: 100px;
      height: 100px;
      background - color: blue;
      transition: width 2s ease - in - out 1s;
    }

    .box:hover {
      width: 200px;
    }
  </style>
</head>

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

</html>

在上述代码中,当鼠标悬停在 .box 元素上时,其宽度会在 2 秒内,以 ease - in - out 的速度曲线,延迟 1 秒从 100px 过渡到 200px。

1.2 关键帧动画(@keyframes

关键帧动画允许我们创建更为复杂的动画序列,通过定义一系列关键帧(@keyframes 规则块内的关键状态)来描述动画的变化过程。

语法

@keyframes animation - name {
  from {
    /* 初始状态 */
  }
  to {
    /* 结束状态 */
  }
}

/* 或者使用百分比定义关键帧 */
@keyframes animation - name {
  0% {
    /* 初始状态 */
  }
  50% {
    /* 中间状态 */
  }
  100% {
    /* 结束状态 */
  }
}

.element {
  animation: animation - name duration timing - function delay iteration - count direction fill - mode;
}
  • @keyframes animation - name:定义动画的名称,在 animation 属性中引用。
  • fromto:分别表示动画的开始和结束状态,等同于 0%100%
  • animation 属性
    • animation - name:指定要应用的关键帧动画的名称。
    • duration:动画持续的时间,单位为秒(s)或毫秒(ms)。
    • timing - function:速度曲线,与 transition 中的取值类似。
    • delay:动画开始前的延迟时间,单位为秒(s)或毫秒(ms)。
    • iteration - count:动画的循环次数。可以是具体的数字,也可以是 infinite 表示无限循环。
    • direction:动画的播放方向,常见值有 normal(正常方向,从开始到结束)、reverse(反向,从结束到开始)、alternate(交替,第一次正常,第二次反向,以此类推)、alternate - reverse(反向交替)。
    • fill - mode:指定动画在播放之前和之后如何应用样式。常见值有 none(默认,动画结束后回到初始状态)、forwards(动画结束后保持最后一帧的状态)、backwards(动画开始前应用第一帧的状态)、both(同时具有 forwardsbackwards 的效果)。

示例

<!DOCTYPE html>
<html lang="en">

<head>
  <style>
    @keyframes slide - right {
      from {
        transform: translateX(0);
      }
      to {
        transform: translateX(200px);
      }
    }

    .box {
      width: 100px;
      height: 100px;
      background - color: green;
      animation: slide - right 3s linear 1s infinite alternate;
    }
  </style>
</head>

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

</html>

在这个例子中,.box 元素会在 3 秒内,以线性速度,延迟 1 秒,无限循环且交替方向地从初始位置向右滑动 200px。

二、JavaScript 与 CSS 动画的交互

了解了 CSS 动画的基础后,我们来看如何使用 JavaScript 来控制这些动画。JavaScript 可以通过操作元素的类名、直接修改 CSS 属性等方式来触发和控制 CSS 动画。

2.1 通过类名控制动画

在 HTML 中,我们可以预先定义好包含动画规则的类,然后使用 JavaScript 动态地添加或移除这些类,从而控制动画的播放。

示例

<!DOCTYPE html>
<html lang="en">

<head>
  <style>
    @keyframes fade - in {
      from {
        opacity: 0;
      }
      to {
        opacity: 1;
      }
    }

    .fade - in - class {
      animation: fade - in 2s ease - in - out;
    }

    .box {
      width: 100px;
      height: 100px;
      background - color: red;
    }
  </style>
</head>

<body>
  <div class="box" id="myBox"></div>
  <button onclick="startAnimation()">开始动画</button>

  <script>
    function startAnimation() {
      const box = document.getElementById('myBox');
      box.classList.add('fade - in - class');
    }
  </script>
</body>

</html>

在上述代码中,当点击按钮时,startAnimation 函数会获取 idmyBox 的元素,并为其添加 fade - in - class 类,从而触发淡入动画。

如果我们想要在动画结束后移除类,可以利用 animationend 事件。

<!DOCTYPE html>
<html lang="en">

<head>
  <style>
    @keyframes fade - in {
      from {
        opacity: 0;
      }
      to {
        opacity: 1;
      }
    }

    .fade - in - class {
      animation: fade - in 2s ease - in - out;
    }

    .box {
      width: 100px;
      height: 100px;
      background - color: red;
    }
  </style>
</head>

<body>
  <div class="box" id="myBox"></div>
  <button onclick="startAnimation()">开始动画</button>

  <script>
    function startAnimation() {
      const box = document.getElementById('myBox');
      box.classList.add('fade - in - class');
      box.addEventListener('animationend', function () {
        this.classList.remove('fade - in - class');
      });
    }
  </script>
</body>

</html>

这样,当淡入动画结束时,fade - in - class 类会被移除,元素回到初始状态。

2.2 直接修改 CSS 属性控制动画

除了通过类名,JavaScript 还可以直接修改元素的 CSS 属性来触发动画。这种方式在需要根据动态数据实时调整动画时非常有用。

示例

<!DOCTYPE html>
<html lang="en">

<head>
  <style>
    .box {
      width: 100px;
      height: 100px;
      background - color: orange;
      transition: width 2s ease - in - out;
    }
  </style>
</head>

<body>
  <div class="box" id="myBox"></div>
  <button onclick="changeWidth()">改变宽度</button>

  <script>
    function changeWidth() {
      const box = document.getElementById('myBox');
      box.style.width = '200px';
    }
  </script>
</body>

</html>

在这个例子中,当点击按钮时,changeWidth 函数会直接修改 myBox 元素的 width 属性,由于元素预先定义了 width 的过渡效果,所以会平滑地过渡到新的宽度。

如果要实现关键帧动画效果,也可以通过 JavaScript 动态修改元素的多个属性。

<!DOCTYPE html>
<html lang="en">

<head>
  <style>
    .box {
      width: 100px;
      height: 100px;
      background - color: purple;
    }
  </style>
</head>

<body>
  <div class="box" id="myBox"></div>
  <button onclick="startComplexAnimation()">开始复杂动画</button>

  <script>
    function startComplexAnimation() {
      const box = document.getElementById('myBox');
      box.style.transform = 'translateX(200px) rotate(360deg)';
      box.style.opacity = '0';
      box.style.transition = 'transform 3s ease - in - out, opacity 3s ease - in - out';
    }
  </script>
</body>

</html>

上述代码通过一次修改 transformopacity 属性,并设置相应的过渡效果,实现了一个元素的移动和旋转同时淡入的复杂动画。

三、使用 JavaScript 创建动态 CSS 动画

有时候,我们需要根据不同的用户输入或实时数据来创建动态的 CSS 动画。这就需要我们通过 JavaScript 动态生成 CSS 规则。

3.1 创建 <style> 元素并添加动画规则

我们可以在 JavaScript 中创建一个 <style> 元素,然后向其中添加 @keyframes 规则和相关的动画类。

示例

<!DOCTYPE html>
<html lang="en">

<head>
</head>

<body>
  <div class="box" id="myBox"></div>
  <button onclick="createDynamicAnimation()">创建动态动画</button>

  <script>
    function createDynamicAnimation() {
      const style = document.createElement('style');
      const keyframes = `
        @keyframes dynamic - animation {
          from {
            transform: translateX(0);
          }
          to {
            transform: translateX(300px);
          }
        }
      `;
      const animationClass = `
        .dynamic - animation - class {
          animation: dynamic - animation 4s linear;
        }
      `;
      style.textContent = keyframes + animationClass;
      document.head.appendChild(style);

      const box = document.getElementById('myBox');
      box.classList.add('dynamic - animation - class');
    }
  </script>
</body>

</html>

在这个例子中,当点击按钮时,createDynamicAnimation 函数会创建一个 <style> 元素,向其中添加自定义的 @keyframes 规则和动画类,然后将该 <style> 元素添加到文档头部,并为 myBox 元素添加动画类,从而触发动态创建的动画。

3.2 使用 CSSStyleSheet 对象

除了创建 <style> 元素,我们还可以使用 CSSStyleSheet 对象来更灵活地管理 CSS 规则。

示例

<!DOCTYPE html>
<html lang="en">

<head>
</head>

<body>
  <div class="box" id="myBox"></div>
  <button onclick="createDynamicAnimationWithCSSStyleSheet()">创建动态动画</button>

  <script>
    function createDynamicAnimationWithCSSStyleSheet() {
      const sheet = new CSSStyleSheet();
      const keyframesRule = `@keyframes dynamic - animation - sheet { from { transform: translateY(0); } to { transform: translateY(200px); } }`;
      const animationClassRule = `.dynamic - animation - sheet - class { animation: dynamic - animation - sheet 3s ease - out; }`;
      sheet.replaceSync(keyframesRule + '\n' + animationClassRule);
      document.adoptedStyleSheets = [sheet];

      const box = document.getElementById('myBox');
      box.classList.add('dynamic - animation - sheet - class');
    }
  </script>
</body>

</html>

在上述代码中,createDynamicAnimationWithCSSStyleSheet 函数使用 CSSStyleSheet 对象创建了新的动画规则,并通过 document.adoptedStyleSheets 将其应用到文档中,然后为 myBox 元素添加相应的动画类,实现动态动画效果。

四、JavaScript 控制动画的高级技巧

在实际应用中,我们还可以运用一些高级技巧来更好地控制 CSS 动画,提升用户体验。

4.1 暂停和恢复动画

有时候,我们需要暂停正在播放的动画,然后在合适的时机恢复。对于过渡动画,由于其本身没有直接的暂停和恢复方法,我们可以通过移除和重新添加过渡效果来模拟暂停和恢复。

示例

<!DOCTYPE html>
<html lang="en">

<head>
  <style>
    .box {
      width: 100px;
      height: 100px;
      background - color: yellow;
      transition: width 5s ease - in - out;
    }
  </style>
</head>

<body>
  <div class="box" id="myBox"></div>
  <button onclick="startTransition()">开始过渡</button>
  <button onclick="pauseTransition()">暂停过渡</button>
  <button onclick="resumeTransition()">恢复过渡</button>

  <script>
    let isPaused = false;
    function startTransition() {
      const box = document.getElementById('myBox');
      box.style.width = '300px';
    }

    function pauseTransition() {
      const box = document.getElementById('myBox');
      if (isPaused) return;
      const currentWidth = box.offsetWidth;
      box.style.transition = 'none';
      box.style.width = currentWidth + 'px';
      isPaused = true;
    }

    function resumeTransition() {
      const box = document.getElementById('myBox');
      if (!isPaused) return;
      box.style.transition = 'width 5s ease - in - out';
      box.style.width = '300px';
      isPaused = false;
    }
  </script>
</body>

</html>

在这个例子中,pauseTransition 函数通过移除过渡效果并记录当前宽度来暂停动画,resumeTransition 函数则通过重新添加过渡效果并恢复目标宽度来恢复动画。

对于关键帧动画,我们可以利用 animation - play - state 属性来暂停和恢复动画。

<!DOCTYPE html>
<html lang="en">

<head>
  <style>
    @keyframes spin {
      from {
        transform: rotate(0deg);
      }
      to {
        transform: rotate(360deg);
      }
    }

    .box {
      width: 100px;
      height: 100px;
      background - color: cyan;
      animation: spin 2s infinite linear;
    }
  </style>
</head>

<body>
  <div class="box" id="myBox"></div>
  <button onclick="pauseAnimation()">暂停动画</button>
  <button onclick="resumeAnimation()">恢复动画</button>

  <script>
    function pauseAnimation() {
      const box = document.getElementById('myBox');
      box.style.animationPlayState = 'paused';
    }

    function resumeAnimation() {
      const box = document.getElementById('myBox');
      box.style.animationPlayState = 'running';
    }
  </script>
</body>

</html>

通过设置 animationPlayState 属性为 pausedrunning,可以轻松地暂停和恢复关键帧动画。

4.2 控制动画的方向和循环次数

我们可以根据用户的操作或程序的逻辑动态地改变动画的方向和循环次数。

示例

<!DOCTYPE html>
<html lang="en">

<head>
  <style>
    @keyframes move - left - right {
      from {
        transform: translateX(0);
      }
      to {
        transform: translateX(200px);
      }
    }

    .box {
      width: 100px;
      height: 100px;
      background - color: magenta;
      animation: move - left - right 3s linear infinite;
    }
  </style>
</head>

<body>
  <div class="box" id="myBox"></div>
  <button onclick="changeAnimationDirection()">改变动画方向</button>
  <button onclick="changeAnimationIteration()">改变循环次数</button>

  <script>
    function changeAnimationDirection() {
      const box = document.getElementById('myBox');
      const currentDirection = box.style.animationDirection;
      if (currentDirection === 'normal' || currentDirection === '') {
        box.style.animationDirection = 'reverse';
      } else {
        box.style.animationDirection = 'normal';
      }
    }

    function changeAnimationIteration() {
      const box = document.getElementById('myBox');
      const currentIteration = box.style.animationIterationCount;
      if (currentIteration === 'infinite' || currentIteration === '') {
        box.style.animationIterationCount = '3';
      } else {
        box.style.animationIterationCount = 'infinite';
      }
    }
  </script>
</body>

</html>

在上述代码中,changeAnimationDirection 函数通过切换 animationDirection 属性来改变动画的方向,changeAnimationIteration 函数通过修改 animationIterationCount 属性来改变动画的循环次数。

4.3 动画的同步和队列

在某些情况下,我们可能需要让多个动画同步播放,或者按顺序依次播放(动画队列)。

动画同步示例

<!DOCTYPE html>
<html lang="en">

<head>
  <style>
    @keyframes fade - in {
      from {
        opacity: 0;
      }
      to {
        opacity: 1;
      }
    }

    @keyframes scale - up {
      from {
        transform: scale(1);
      }
      to {
        transform: scale(1.5);
      }
    }

    .box1 {
      width: 100px;
      height: 100px;
      background - color: lightblue;
      animation: fade - in 2s ease - in - out, scale - up 2s ease - in - out;
    }

    .box2 {
      width: 100px;
      height: 100px;
      background - color: lightgreen;
      animation: fade - in 2s ease - in - out 1s, scale - up 2s ease - in - out 1s;
    }
  </style>
</head>

<body>
  <div class="box1"></div>
  <div class="box2"></div>
</body>

</html>

在这个例子中,.box1 的淡入和放大动画同时开始,而 .box2 的两个动画延迟 1 秒开始,从而实现了动画的同步效果。

动画队列示例

<!DOCTYPE html>
<html lang="en">

<head>
  <style>
    @keyframes slide - left {
      from {
        transform: translateX(0);
      }
      to {
        transform: translateX(-200px);
      }
    }

    @keyframes fade - out {
      from {
        opacity: 1;
      }
      to {
        opacity: 0;
      }
    }

    .box {
      width: 100px;
      height: 100px;
      background - color: pink;
    }
  </style>
</head>

<body>
  <div class="box" id="myBox"></div>
  <button onclick="startQueueAnimation()">开始队列动画</button>

  <script>
    function startQueueAnimation() {
      const box = document.getElementById('myBox');
      box.style.animation ='slide - left 2s ease - out';
      box.addEventListener('animationend', function () {
        this.style.animation = 'fade - out 1s ease - out';
      });
    }
  </script>
</body>

</html>

在上述代码中,当点击按钮时,myBox 元素先执行向左滑动的动画,动画结束后,再执行淡出动画,实现了动画队列的效果。

通过上述方法,我们可以利用 JavaScript 灵活地控制 CSS 动画,创造出丰富多样的动态效果,为网页和应用程序增添更多的交互性和趣味性。无论是简单的过渡动画还是复杂的关键帧动画序列,JavaScript 与 CSS 的结合都为开发者提供了强大的工具来实现创意十足的动画设计。