餓了么面試官:實(shí)現(xiàn)一下 Element-UI 官網(wǎng)的主題切換動(dòng)畫!
前言
大家好,我是林三心,用最通俗易懂的話講最難的知識(shí)點(diǎn)是我的座右銘,基礎(chǔ)是進(jìn)階的前提是我的初心!
最近看到 ElementPlus 官網(wǎng)上的切換主題方式非常有趣,這是一個(gè)過渡的動(dòng)畫效果。
圖片
所以在網(wǎng)上查了一番,找到基本的實(shí)現(xiàn)方法。
實(shí)現(xiàn)
基本效果
首先我們起一個(gè) html 文件,寫一個(gè)按鈕,以及簡單的背景顏色切換,來模擬主題的切換
圖片
可以看到實(shí)現(xiàn)了最簡單的主題切換效果。
圖片
document.startViewTransition
想要實(shí)現(xiàn)過渡效果,需要先用到一個(gè) JavaScript 的原生方法:document.startViewTransition。
這個(gè)方法是用來做動(dòng)畫過渡效果的。
圖片
通過調(diào)用 API,讓瀏覽器為新舊兩種不同視圖分別捕獲并建立了快照 (即 ::view-transition-old(root)舊快照 和 ::view-transition-new(root) 新快照),而后新舊兩快照在 ::view-transition-image-pair(root) 容器中完成轉(zhuǎn)場動(dòng)畫的過渡。動(dòng)畫結(jié)束后則刪除其相關(guān)偽元素 (快照和容器)
圖片
過渡動(dòng)畫效果
我們可以應(yīng)用一下這個(gè) API
圖片
現(xiàn)在去切換主題顏色,發(fā)現(xiàn)有過渡效果了~
圖片
圓形擴(kuò)散過渡動(dòng)畫
接下來實(shí)現(xiàn)圓形過渡的效果,其實(shí)這個(gè)動(dòng)畫最終是展示::view-transition-new(root)這個(gè)偽元素,所以我們只需要讓這個(gè)偽元素有原型擴(kuò)散的過渡動(dòng)畫即可~
那圓形擴(kuò)散動(dòng)畫咋做呢?其實(shí)很簡單,只需要將偽元素的半徑,從0 -> 100%即可
圖片
代碼如下:
圖片
并且我們需要取消掉 document.startViewTransition默認(rèn)的動(dòng)畫效果,不然它會(huì)導(dǎo)致我們自定義的動(dòng)畫效果無效~
圖片
最終得到圓形擴(kuò)散的效果
圖片
完整代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
:root {
/* 默認(rèn)亮主題 */
--bg-color: #fff;
background-color: var(--bg-color);
}
:root.dark {
/* 暗主題 */
--bg-color: #000;
}
::view-transition-new(root),
::view-transition-old(root) {
/* 關(guān)閉默認(rèn)動(dòng)畫 */
animation: none;
}
</style>
</head>
<body>
<button id="themeButton">切換主題</button>
<script>
const themeButton = document.getElementById("themeButton");
themeButton.addEventListener("click", (e) => {
// 執(zhí)行切換主題的操作
const transition = document.startViewTransition(() => {
// 動(dòng)畫過渡切換主題色
document.documentElement.classList.toggle("dark");
});
// document.startViewTransition 的 ready 返回一個(gè) Promise
transition.ready.then(() => {
// 獲取鼠標(biāo)的坐標(biāo)
const { clientX, clientY } = e;
// 計(jì)算最大半徑
const radius = Math.hypot(
Math.max(clientX, innerWidth - clientX),
Math.max(clientY, innerHeight - clientY)
);
// 圓形動(dòng)畫擴(kuò)散開始
document.documentElement.animate(
{
clipPath: [
`circle(0% at ${clientX}px ${clientY}px)`,
`circle(${radius}px at ${clientX}px ${clientY}px)`,
],
},
// 設(shè)置時(shí)間,已經(jīng)目標(biāo)偽元素
{
duration: 300,
pseudoElement: "::view-transition-new(root)",
}
);
});
});
</script>
</body>
</html>