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