Skip to content

Commit 62215d1

Browse files
authored
Merge pull request #93 from stefan-ysh/main
Add view transition animation to the theme toggle functionality
2 parents a72c829 + ba35bb9 commit 62215d1

File tree

2 files changed

+68
-6
lines changed

2 files changed

+68
-6
lines changed

src/components/Themetoggle.astro

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,54 @@ const listenColorSchema = () => {
7777
listenColorSchema()
7878
toogleThemeCircle()
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
}
87129
themeToggle.addEventListener('click', handleToggleClick)
88130
</script>

src/layouts/Layout.astro

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,27 @@ const { title } = Astro.props;
7878
min-height: calc(100vh - 4.5rem);
7979
}
8080
}
81+
::view-transition-old(root),
82+
::view-transition-new(root) {
83+
animation: none;
84+
mix-blend-mode: normal;
85+
}
86+
87+
.dark::view-transition-old(root) {
88+
z-index: 1;
89+
}
90+
91+
.dark::view-transition-new(root) {
92+
z-index: 999;
93+
}
94+
95+
::view-transition-old(root) {
96+
z-index: 999;
97+
}
8198

99+
::view-transition-new(root) {
100+
z-index: 1;
101+
}
82102
</style>
83103

84104
<script>

0 commit comments

Comments
 (0)