forked from babaohuang/GeminiProChat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Themetoggle.astro
130 lines (122 loc) · 4.12 KB
/
Themetoggle.astro
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
<div id="themeToggle" class="flex items-center justify-center w-10 h-10 rounded-md transition-colors hover:bg-slate/10">
<svg class="theme_toggle_svg" width="1.2em" height="1.2em" viewBox="0 0 24 24" color="#858585" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke="currentColor">
<mask id="myMask">
<rect x="0" y="0" width="100%" height="100%" fill="white"></rect>
<circle class="theme_toggle_circle1" fill="black" cx="100%" cy="0%"></circle>
</mask>
<circle class="theme_toggle_circle2" cx="12" cy="12" fill="#858585" mask="url(#myMask)"></circle>
<g class="theme_toggle_g" stroke="currentColor" opacity="1">
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</g>
</svg>
</div>
<style>
#themeToggle {
border: 0;
cursor: pointer;
}
.theme_toggle_circle1 {
transition: cx .5s, cy .5s;
cx: 100%;
cy: 0%
}
.theme_toggle_circle2 {
transition: r .3s;
}
.theme_toggle_svg {
transition: transform .5s cubic-bezier(0.68, -0.55, 0.27, 1.55);
transform: rotate(90deg);
}
.theme_toggle_g {
transition: opacity .5s;
opacity: 1;
}
:global(html.dark) #themeToggle .theme_toggle_circle1 {
cx: 50%;
cy: 23%;
}
:global(html.dark) #themeToggle .theme_toggle_svg {
transform: rotate(40deg);
}
:global(html.dark) #themeToggle .theme_toggle_g {
opacity: 0;
}
</style>
<script>
const themeToggle = document.getElementById('themeToggle')
const themeCircle1 = document.querySelector('.theme_toggle_circle1')
const themeCircle2 = document.querySelector('.theme_toggle_circle2')
const toogleThemeCircle = () => {
const darkMode = document.documentElement.classList.contains('dark') ?? localStorage.getItem('theme') === 'dark'
if (darkMode) {
themeCircle1.setAttribute('r', '9')
themeCircle2.setAttribute('r', '9')
} else {
themeCircle1.setAttribute('r', '5')
themeCircle2.setAttribute('r', '5')
}
}
const listenColorSchema = () => {
const colorSchema = window.matchMedia('(prefers-color-scheme: dark)')
colorSchema.addEventListener('change', () => {
document.documentElement.classList.toggle('dark', colorSchema.matches)
toogleThemeCircle()
})
}
listenColorSchema()
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)
</script>