From 3aeed36e0a7321d3cada8d519c0d8aa4831e5ddc Mon Sep 17 00:00:00 2001 From: gjssss Date: Mon, 19 Feb 2024 13:00:41 +0800 Subject: [PATCH] feat: add toggle theme --- app.vue | 21 +++++++++++++++ components/Navigation.vue | 17 +++++++++++-- composables/toggleTheme.ts | 52 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 composables/toggleTheme.ts diff --git a/app.vue b/app.vue index b67df84..2ef9938 100644 --- a/app.vue +++ b/app.vue @@ -26,4 +26,25 @@ html.dark { background: #222; color: white; } +html { + background: #fff; +} + +::view-transition-old(root), +::view-transition-new(root) { + animation: none; + mix-blend-mode: normal; +} +::view-transition-old(root) { + z-index: 1; +} +::view-transition-new(root) { + z-index: 9999; +} +.dark::view-transition-old(root) { + z-index: 9999; +} +.dark::view-transition-new(root) { + z-index: 1; +} diff --git a/components/Navigation.vue b/components/Navigation.vue index b299016..2980a33 100644 --- a/components/Navigation.vue +++ b/components/Navigation.vue @@ -3,21 +3,34 @@ const { data: navigation } = await useAsyncData('nav', async () => { const data = await fetchContentNavigation() return data.filter(item => item.children) }) +const colorMode = useColorMode() diff --git a/composables/toggleTheme.ts b/composables/toggleTheme.ts new file mode 100644 index 0000000..5539224 --- /dev/null +++ b/composables/toggleTheme.ts @@ -0,0 +1,52 @@ +const colorMode = useColorMode() + +function toggle() { + colorMode.preference = colorMode.preference === 'dark' ? 'light' : 'dark' +} +/** + * Credit to [@hooray](https://github.com/hooray) + * @see https://github.com/vuejs/vitepress/pull/2347 + */ +export function toggleDark(event: MouseEvent) { + // @ts-expect-error experimental API + const isAppearanceTransition = document.startViewTransition + && !window.matchMedia('(prefers-reduced-motion: reduce)').matches + + if (!isAppearanceTransition) { + toggle() + 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 () => { + toggle() + 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: colorMode.preference === 'dark' + ? [...clipPath].reverse() + : clipPath, + }, + { + duration: 400, + easing: 'ease-out', + pseudoElement: colorMode.preference === 'dark' + ? '::view-transition-old(root)' + : '::view-transition-new(root)', + }, + ) + }) +}