视图过渡动画
# 概述
网站的主题切换无非就是文字、背景图片或者颜色,我们可以先来看下 Element UI 官网的切换主题的动效:
# 实现的两种方式
# CSS 为主
<script setup>
const changeTheme = (e) => {
if (document.startViewTransition) {
document.startViewTransition(() => {
document.documentElement.classList.toggle("dark");
});
} else {
document.documentElement.classList.toggle("dark");
}
};
onMounted(() => {
const target = document.querySelector(".target1");
const { x, y } = target.getBoundingClientRect();
document.documentElement.style.setProperty("--x", x + "px");
document.documentElement.style.setProperty("--y", y + "px");
});
</script>
<style>
::view-transition-old(root) {
animation: none;
}
::view-transition-new(root) {
mix-blend-mode: normal;
animation: clip 1s ease-in;
}
@keyframes clip {
from {
clip-path: circle(0% at var(--x) var(--y));
}
to {
clip-path: circle(100% at var(--x) var(--y));
}
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# JS 实现
<script setup>
const changeTheme = (e) => {
if (document.startViewTransition) {
const transition = document.startViewTransition(() => {
document.documentElement.classList.toggle("dark");
});
transition.ready.then(() => {
const target = document.querySelector(".target");
const { x, y } = target.getBoundingClientRect();
const radius = Math.hypot(
Math.max(innerWidth - x, x),
Math.max(innerHeight - y, y)
);
const clipPath = [
`circle(0px at ${x}px ${y}px)`,
`circle(${radius}px at ${x}px ${y}px)`,
];
const isDark = !document.documentElement.classList.contains("dark");
document.documentElement.animate(
{ clipPath: isDark ? clipPath : clipPath.reverse() },
{
duration: 1500,
pseudoElement: isDark
? "::view-transition-new(root)"
: "::view-transition-old(root)",
}
);
});
} else {
document.documentElement.classList.toggle("dark");
}
};
</script>
<style>
::view-transition-old(root) {
animation: none;
}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 总结
第一种方式就是定义了一个帧景动画,然后通过 startViewTransition
方法来触发动画。而第二种则是在startViewTransition
的ready
阶段,通过document.documentElement.animate
自定义动画实现
startViewTransition`是View Transition API 的一部分,它允许我们定义一个动画,然后让浏览器来执行这个动画。
# View Transition API
View Transition API 提供了一种机制,可以在更新 DOM 内容的同时,轻松过地创建不同 DOM 状态之间地动画过渡,同时还可以在单个步骤中更新 DOM 内容.
以往在 SPA 中,状态之间的过渡编写较多的代码来解决几个问题:
- 处理新旧内容的加载和定位
- 为新旧状态添加动画以创建过渡
- 防止用户与旧内容的意外交互而导致的问题
- 完成过渡后删除旧内容
而View Transition API就提供了一种更简单的方法来处理必须的 DOM 更新和过渡动画
视图过渡过程
- 调用
document.startViewTransition()
,API 会截取当前页面的屏幕截图 - 执行
startViewTransition()
中的回调函数,这会导致 DOM 发生更改,当回调函数成功运行时,updateCallbackDone
API 兑现,可以响应 DOM 更新 - API 会捕获页面的新状态并实时展示
- API 构造一个具有以下结构的伪元素树
::view-transition // 视图过渡叠加层的根元素,它包含所有试图过渡且位于所有页面内容的顶部
└─ ::view-transition-group(root) // 视图过渡分组元素,它包含所有视图过渡元素
└─ ::view-transition-image-pair(root) // 视图过渡图像对元素,它包含两个子元素,分别表示旧页面和新页面
├─ ::view-transition-old(root) // 视图过渡旧元素,它表示旧页面的屏幕截图
└─ ::view-transition-new(root) // 视图过渡新元素,它表示新页面
1
2
3
4
5
2
3
4
5
当过渡动画即将运行时,ready
Promise 兑现,可以执行某些操作,比如自定义动画
- 旧页面视图到新页面的视图过渡动画
- 当过渡动画结束时,
finished
Promise 兑现,可以响应某些操作
# document.documentElement.animate
Element
接口的animate
方法是创建一个Animation
的便捷方法并将其应用于元素,然后运行动画。另外该方法会将Animation
对象实例返回,该实例可以控制动画的播放、暂停、停止等。
用法如下:
element.animate(keyframes, options);
1
参数说明:
keyframes
: 动画关键帧,可以是一个对象数组,也可以是一个KeyframeEffect
对象options
: 动画选项,可以是一个对象,也可以是一个数字,数字表示动画持续时间
编辑 (opens new window)
上次更新: 2024/08/24, 14:18:38