From ba35bb95e5fe0657e05487b224660622bb63cb4a Mon Sep 17 00:00:00 2001 From: yshuai Date: Mon, 25 Dec 2023 10:03:20 +0800 Subject: [PATCH] feat:add view transition animation to the theme toggle functionality --- src/components/Themetoggle.astro | 54 ++++++++++++++++++++++++++++---- src/layouts/Layout.astro | 20 ++++++++++++ 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/src/components/Themetoggle.astro b/src/components/Themetoggle.astro index 145e1549..a47704cb 100644 --- a/src/components/Themetoggle.astro +++ b/src/components/Themetoggle.astro @@ -77,12 +77,54 @@ const listenColorSchema = () => { listenColorSchema() toogleThemeCircle() -const handleToggleClick = () => { - const element = document.documentElement - element.classList.toggle('dark') - const isDark = element.classList.contains('dark') - localStorage.setItem('theme', isDark ? 'dark' : 'light') - toogleThemeCircle() +const handleToggleClick = ({ x, y }) => { + const root = document.documentElement + const isDark = root.classList.contains('dark') + localStorage.setItem('theme', isDark ? 'light' : 'dark') + + // check if the current browser supports viewtransition API + const isAppearanceTransition + // @ts-expect-error: Transition API + = document.startViewTransition + && !window.matchMedia('(prefers-reduced-motion: reduce)').matches + + // if it is not supported, just toggle the class directly + if (!isAppearanceTransition) { + toogleThemeCircle() + root.classList.toggle('dark') + } else { + // if it is supported, use the transition API to animate the theme change + const endRadius = Math.hypot( + Math.max(x, innerWidth - x), + Math.max(y, innerHeight - y), + ) + + // @ts-expect-error: Transition API + const transition = document.startViewTransition(() => { + root.classList.toggle('dark') + toogleThemeCircle() + }) + transition.ready.then(() => { + const clipPath = [ + `circle(0px at ${x}px ${y}px)`, + `circle(${endRadius}px at ${x}px ${y}px)`, + ] + const _c = !isDark ? clipPath : [...clipPath].reverse() + const pseudoElement = !isDark + ? '::view-transition-new(root)' + : '::view-transition-old(root)' + document.documentElement.animate( + { + clipPath: _c, + }, + { + duration: 500, + easing: 'ease-in', + pseudoElement, + }, + ) + }) + } } themeToggle.addEventListener('click', handleToggleClick) diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index a36f0316..2baeedd0 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -78,7 +78,27 @@ const { title } = Astro.props; min-height: calc(100vh - 4.5rem); } } + ::view-transition-old(root), + ::view-transition-new(root) { + animation: none; + mix-blend-mode: normal; + } + + .dark::view-transition-old(root) { + z-index: 1; + } + + .dark::view-transition-new(root) { + z-index: 999; + } + + ::view-transition-old(root) { + z-index: 999; + } + ::view-transition-new(root) { + z-index: 1; + }