技术前端开发View Transition API

前言

router-transition

上面是一个路由切换的动画。以前,我们想要实现这个效果,需要很多 hack 的方法。但是现在,不管你使用的是什么框架,什么路由,只需要几行代码就可以实现这个效果。

这种革命性的改变得益于浏览器原生 API 的支持。而在 React 生态中,这个特性的支持更是让人兴奋。

随着 Add ViewTransition Component 的合入, React 官方终于支持了 View Transition API,这是自从 react 宣布 react hooks 以后我最喜欢的一个特性。

为什么需要 View Transition API

react 中,想要实现离场动画比入场动画要麻烦很多,因为 react 的组件在卸载的时候,节点会直接从 dom 中移除,而不会等待动画结束再移除。

在以前的 如何实现优雅的离场动画 这篇文章中, 我介绍过使用 framer-motion 来实现离场动画。 主要原理是记录当前需要执行离场动画的组件,在组件卸载阻默认行为,直到离场动画完成以后再卸载组件。

要完成这个过程,需要大量的 JS 代码。而 View Transition APIWEB API, 核心原理就是构建了两种新的伪元素

  • ::view-transition-old(name)
  • ::view-transition-new(name)

用来保存当前视图的快照,他们只短暂存在于视图过渡期间,随后立即销毁,我们可以直接使用 CSS 来指定样式。完美解决了以前离场动画的痛点。

使用 View Transition API 实现几个简单的动画

1. 路由切换动画

文章开头的路由切换动画,一共就以下几行代码.

import { unstable_ViewTransition as ViewTransition } from "react";
 
// 使用 ViewTransition 包裹需要执行动画的元素
<ViewTransition name="page-transition">
  {children}
</ViewTransition>
 

此时自动就会给路由切换加上一个淡入淡出的动画,我们可以使用 CSS 来指定动画的样式。

::view-transition-old(page-transition) {
  animation: slide-out 0.5s ease-out both;
}
 
::view-transition-new(page-transition) {
  animation: slide-in 0.5s ease-out both;
  animation-delay: 300ms;
}

2. 元素删除动画

delete-transition
import { unstable_ViewTransition as ViewTransition } from "react";
 
const initialBoxes = [
  { id: 1, name: "nextjs" },
  { id: 2, name: "nestjs" },
  { id: 3, name: "hono" },
  { id: 4, name: "remix" },
  { id: 5, name: "midway" },
];
function deleteTransition() {
 const [box, setBox] = useState(initialBoxes);
 
 const handleDelete = (id: number) => {
 startTransition(() => {
     setBoxes(boxes.filter((box) => box.id !== id));
  });
 };
 
return (
    <div className="flex flex-wrap gap-4">
    {boxes.map((box) => (
       <ViewTransition key={box.id} name={box.name}>
        <div className="relative w-32 h-32 border-2 border-blue-500 rounded-lg flex items-center justify-center">
         <button
            onClick={() => handleDelete(box.id)}
            className="absolute top-2 right-2 text-blue-500 w-6 h-6 rounded-full cursor-pointer"
            >
            ×
          </button>
            {box.name}
        </div>
       </ViewTransition>
    ))}
    </div>
  );
}

相比于路由切换的动画,除了 ViewTransition 包裹元素以外,还需要使用 startTransition 来包裹 setState 的调用,就可以完成一个非常平滑的元素删除动画了。

为什么之前 react 不支持 View Transition API

react 中是有虚拟 dom 和批量更新机制的,而 View Transition API 则是期望在其回调函数中立即发生 DOM 变化。因此可能在 react 完成页面更新的时候, 生成的快照早已经被销毁了,所以自然就不起作用了。

react 的支持会对以后有什么影响

虽然目前这个特性还处于 experimental 阶段, 你需要使用

 "react": "experimental",
 "react-dom": "experimental",

来体验这个特性。如果你使用的是 TypeScript ,你还需要你的 tsconfig.json 中添加以下这段代码

"types": ["react/experimental"]

目前还有一些问题,离稳定还有一段时间。但是我相信在不久的将来,各大路由库、框架、动画库都会支持这个特性。 我们使用的 react 开发的应用会像原生 APP 应用一样,拥有非常丝滑的动画效果。