网页换肤“动”起来:View Transition API 的妙用

199 阅读2分钟

前言

最近在研究网页主题切换的动画效果时,我发现了一个非常实用的 Web API —— View Transition API,它可以让网页的主题切换变得丝滑流畅,给用户带来更佳的视觉体验。今天,就来和大家分享一下如何利用这个 API 实现 Element-Plus 的主题切换动画效果。

初识 View Transition API

View Transition API 是浏览器原生提供的一个强大工具,能够帮助开发者轻松实现页面状态切换时的过渡动画。它的核心思想是:当页面内容发生较大变化时(比如主题切换、页面跳转等),浏览器会自动为新旧内容分别创建快照,然后通过这些快照完成平滑的过渡效果。

这个 API 的使用相对简单,主要依赖于 document.startViewTransition() 方法。调用这个方法后,浏览器会捕获当前页面视图作为旧快照(::view-transition-old(root)),在回调函数中修改页面内容后,再捕获新视图作为新快照(::view-transition-new(root)),最后在 ::view-transition-image-pair(root) 容器中完成动画过渡。

基础主题切换实现

我们先从最简单的主题切换开始。创建一个 HTML 文件,添加一个按钮,并通过切换 CSS 变量来改变背景颜色,模拟主题的明暗切换。

<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>主题切换示例</title>
<style>
    :root {
      --bg-color#fff;
      backgroundvar(--bg-color);
    }

    :root.dark {
      --bg-color#000;
    }
  </style>
</head>
<body>
<button id="themeButton">切换主题</button>
<script>
    const themeButton = document.getElementById('themeButton');
    themeButton.addEventListener('click'() => {
      document.documentElement.classList.toggle('dark');
    });
  </script>
</body>
</html>

添加过渡动画

接下来,我们利用 View Transition API 为切换过程添加过渡动画。在按钮的点击事件中,调用 document.startViewTransition() 方法,将主题切换的逻辑放入其回调函数中。

themeButton.addEventListener('click'() => {
  document.startViewTransition(() => {
    document.documentElement.classList.toggle('dark');
  });
});

这样,当我们点击按钮时,页面就会出现一个默认的过渡动画效果。

自定义圆形扩散动画

为了让动画效果更具特色,我们可以自定义一个圆形扩散动画。在调用 document.startViewTransition() 后,通过其返回的 Promise 对象,在动画开始时获取鼠标点击位置的坐标,然后计算出圆形扩散的最大半径。最后,利用 CSS 的 clipPath 属性结合 document.documentElement.animate() 方法,实现圆形从点击点扩散的动画效果。

themeButton.addEventListener('click'(e) => {
const transition = document.startViewTransition(() => {
    document.documentElement.classList.toggle('dark');
  });

  transition.ready.then(() => {
    const { clientX, clientY } = e;
    const radius = Math.hypot(
      Math.max(clientX, innerWidth - clientX),
      Math.max(clientY, innerHeight - clientY)
    );

    document.documentElement.animate(
      [
        { clipPath`circle(0% at ${clientX}px ${clientY}px)` },
        { clipPath`circle(${radius}px at ${clientX}px ${clientY}px)` }
      ],
      {
        duration300,
        pseudoElement"::view-transition-new(root)"
      }
    );
  });
});

为了确保自定义动画正常工作,还需要在 CSS 中关闭 View Transition API 的默认动画效果:

::view-transition-new(root),
::view-transition-old(root) {
  animation: none;
}

最终完整代码

将以上内容整合,得到完整的主题切换动画实现代码:

<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>主题切换动画示例</title>
<style>
    :root {
      --bg-color#fff;
      background-colorvar(--bg-color);
    }

    :root.dark {
      --bg-color#000;
    }

    ::view-transition-new(root),
    ::view-transition-old(root) {
      animation: none;
    }
  </style>
</head>
<body>
<button id="themeButton">切换主题</button>
<script>
    const themeButton = document.getElementById("themeButton");
    themeButton.addEventListener("click"(e) => {
      const transition = document.startViewTransition(() => {
        document.documentElement.classList.toggle("dark");
      });

      transition.ready.then(() => {
        const { clientX, clientY } = e;
        const radius = Math.hypot(
          Math.max(clientX, innerWidth - clientX),
          Math.max(clientY, innerHeight - clientY)
        );

        document.documentElement.animate(
          {
            clipPath: [
              `circle(0% at ${clientX}px ${clientY}px)`,
              `circle(${radius}px at ${clientX}px ${clientY}px)`,
            ],
          },
          {
            duration500,
            pseudoElement"::view-transition-new(root)",
          }
        );
      });
    });
  </script>
</body>
</html>

通过以上步骤,我们成功实现了一个具有圆形扩散过渡效果的网页主题切换功能。View Transition API 的强大之处在于它简化了复杂动画的实现过程,让开发者能够以较低的成本为用户提供换肤动画效果