feat(next): soft appearance toggle animation (#238)

pull/286/head
Rizumu Ayaka 2023-12-22 18:55:13 +08:00
parent 3e8db3be9d
commit ed3588f658
3 changed files with 97 additions and 11 deletions

View File

@ -7,6 +7,7 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
AppearanceToggle: typeof import('./theme/components/AppearanceToggle.vue')['default']
Changelog: typeof import('./theme/components/Changelog.vue')['default']
HomeContent: typeof import('./theme/components/HomeContent.vue')['default']
PageInfo: typeof import('./theme/components/PageInfo.vue')['default']

View File

@ -0,0 +1,82 @@
<script setup lang="ts">
import { useData } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import { nextTick, provide, onMounted } from 'vue'
const { isDark } = useData()
const enableTransitions = () =>
'startViewTransition' in document &&
window.matchMedia('(prefers-reduced-motion: no-preference)').matches
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
provide('toggle-appearance', async ({ clientX: x, clientY: y }: MouseEvent) => {
if (!enableTransitions()) {
isDark.value = !isDark.value
return
}
if (typeof (document as any).startViewTransition !== 'function') {
return
}
if (!document.documentElement.classList.contains('VPSwitchAppearance-ViewTransition')) {
document.documentElement.classList.add('VPSwitchAppearance-ViewTransition')
}
const clipPath = [
`circle(0px at ${x}px ${y}px)`,
`circle(${Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y)
)}px at ${x}px ${y}px)`
]
await (document as any).startViewTransition(async () => {
isDark.value = !isDark.value
await nextTick()
}).ready
document.documentElement.animate(
{ clipPath: isDark.value ? clipPath.reverse() : clipPath },
{
duration: 500,
easing: 'ease-in',
pseudoElement: `::view-transition-${isDark.value ? 'old' : 'new'}(root)`
}
)
})
</script>
<template>
<slot />
</template>
<style>
::view-transition-old(root),
::view-transition-new(root) {
animation: none;
mix-blend-mode: normal;
}
::view-transition-old(root),
.dark::view-transition-new(root) {
z-index: 1;
}
::view-transition-new(root),
.dark::view-transition-old(root) {
z-index: 9999;
}
.VPSwitchAppearance-ViewTransition .VPSwitchAppearance .check {
transition: transform 350ms 0ms !important;
}
.VPSwitchAppearance-ViewTransition.dark .VPSwitchAppearance .check {
transition: transform 350ms 500ms !important;
}
</style>

View File

@ -2,6 +2,7 @@
import { h } from 'vue'
import type { Theme } from 'vitepress'
import DefaultTheme from 'vitepress/theme'
import AppearanceToggle from './components/AppearanceToggle.vue'
import {
NolebaseEnhancedReadabilitiesPlugin,
@ -22,7 +23,8 @@ import 'uno.css'
export default {
extends: DefaultTheme,
Layout: () => {
return h(DefaultTheme.Layout, null, {
return h(AppearanceToggle, null, {
'default': () => h(DefaultTheme.Layout, null, {
// https://vitepress.dev/guide/extending-default-theme#layout-slots
'doc-top': () => [
h(NolebaseHighlightTargetedHeading),
@ -33,6 +35,7 @@ export default {
'nav-screen-content-after': () => [
h(NolebaseEnhancedReadabilitiesScreenMenu),
],
}),
})
},
enhanceApp({ app }) {