docs: add keyboard control switch theme (#17664)

* feat(docs): add keyboard control switch theme

* chore: optimizing the code

---------

Co-authored-by: qiang <qw13131wang@gmail.com>
This commit is contained in:
DonV 2024-07-31 15:47:33 +08:00 committed by GitHub
parent f2dc4ec624
commit 6dfc646385
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -3,8 +3,10 @@ import { nextTick, ref, watch } from 'vue'
import { isDark, toggleDark } from '../../composables/dark'
import DarkIcon from '../icons/dark.vue'
import LightIcon from '../icons/light.vue'
import type { SwitchInstance } from 'element-plus'
const darkMode = ref(isDark.value)
const switchRef = ref<SwitchInstance>()
watch(
() => darkMode.value,
@ -13,64 +15,63 @@ watch(
}
)
let resolveFn: (value: boolean | PromiseLike<boolean>) => void
const switchTheme = (event: MouseEvent) => {
const isAppearanceTransition =
// @ts-expect-error
document.startViewTransition &&
!window.matchMedia('(prefers-reduced-motion: reduce)').matches
if (!isAppearanceTransition || !event) {
resolveFn(true)
return
}
const x = event.clientX
const y = event.clientY
const endRadius = Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y)
)
// @ts-expect-error: Transition API
const transition = document.startViewTransition(async () => {
resolveFn(true)
await nextTick()
})
transition.ready.then(() => {
const clipPath = [
`circle(0px at ${x}px ${y}px)`,
`circle(${endRadius}px at ${x}px ${y}px)`,
]
document.documentElement.animate(
{
clipPath: isDark.value ? [...clipPath].reverse() : clipPath,
},
{
duration: 400,
easing: 'ease-in',
pseudoElement: isDark.value
? '::view-transition-old(root)'
: '::view-transition-new(root)',
}
const beforeChange = () => {
return new Promise<boolean>((resolve) => {
const isAppearanceTransition =
// @ts-expect-error
document.startViewTransition &&
!window.matchMedia('(prefers-reduced-motion: reduce)').matches
if (!isAppearanceTransition) {
resolve(true)
return
}
const switchElement = switchRef.value?.$el
const rect = switchElement.getBoundingClientRect()
const x = rect.left + rect.width / 2
const y = rect.top + rect.height / 2
const endRadius = Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y)
)
})
}
const beforeChange = (): Promise<boolean> => {
return new Promise((resolve) => {
resolveFn = resolve
// @ts-expect-error: Transition API
const transition = document.startViewTransition(async () => {
resolve(true)
await nextTick()
})
transition.ready.then(() => {
const clipPath = [
`circle(0px at ${x}px ${y}px)`,
`circle(${endRadius}px at ${x}px ${y}px)`,
]
document.documentElement.animate(
{
clipPath: isDark.value ? [...clipPath].reverse() : clipPath,
},
{
duration: 400,
easing: 'ease-in',
pseudoElement: isDark.value
? '::view-transition-old(root)'
: '::view-transition-new(root)',
}
)
})
})
}
</script>
<template>
<div @click.stop="switchTheme">
<ClientOnly>
<el-switch
v-model="darkMode"
:before-change="beforeChange"
:active-action-icon="DarkIcon"
:inactive-action-icon="LightIcon"
/>
</ClientOnly>
</div>
<ClientOnly>
<el-switch
ref="switchRef"
v-model="darkMode"
:before-change="beforeChange"
:active-action-icon="DarkIcon"
:inactive-action-icon="LightIcon"
/>
</ClientOnly>
</template>
<style lang="scss" scoped>