@@ -77,12 +77,54 @@ const listenColorSchema = () => {
7777listenColorSchema()
7878toogleThemeCircle()
7979
80- const handleToggleClick = () => {
81- const element = document.documentElement
82- element.classList.toggle('dark')
83- const isDark = element.classList.contains('dark')
84- localStorage.setItem('theme', isDark ? 'dark' : 'light')
85- toogleThemeCircle()
80+ const handleToggleClick = ({ x, y }) => {
81+ const root = document.documentElement
82+ const isDark = root.classList.contains('dark')
83+ localStorage.setItem('theme', isDark ? 'light' : 'dark')
84+
85+ // check if the current browser supports viewtransition API
86+ const isAppearanceTransition
87+ // @ts-expect-error: Transition API
88+ = document.startViewTransition
89+ && !window.matchMedia('(prefers-reduced-motion: reduce)').matches
90+
91+ // if it is not supported, just toggle the class directly
92+ if (!isAppearanceTransition) {
93+ toogleThemeCircle()
94+ root.classList.toggle('dark')
95+ } else {
96+ // if it is supported, use the transition API to animate the theme change
97+ const endRadius = Math.hypot(
98+ Math.max(x, innerWidth - x),
99+ Math.max(y, innerHeight - y),
100+ )
101+
102+ // @ts-expect-error: Transition API
103+ const transition = document.startViewTransition(() => {
104+ root.classList.toggle('dark')
105+ toogleThemeCircle()
106+ })
107+ transition.ready.then(() => {
108+ const clipPath = [
109+ `circle(0px at ${x}px ${y}px)`,
110+ `circle(${endRadius}px at ${x}px ${y}px)`,
111+ ]
112+ const _c = !isDark ? clipPath : [...clipPath].reverse()
113+ const pseudoElement = !isDark
114+ ? '::view-transition-new(root)'
115+ : '::view-transition-old(root)'
116+ document.documentElement.animate(
117+ {
118+ clipPath: _c,
119+ },
120+ {
121+ duration: 500,
122+ easing: 'ease-in',
123+ pseudoElement,
124+ },
125+ )
126+ })
127+ }
86128}
87129themeToggle.addEventListener('click', handleToggleClick)
88130</script >
0 commit comments