Add view transition when changing themes. (#29)

This commit is contained in:
Alessandro Jean 2023-09-08 18:28:58 -03:00 committed by GitHub
parent 0ae836ed2e
commit cd61ac174f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 153 additions and 1 deletions

View File

@ -55,6 +55,12 @@ export default defineConfig({
new URL("./theme/components/CustomNavScreenMenu.vue", import.meta.url), new URL("./theme/components/CustomNavScreenMenu.vue", import.meta.url),
), ),
}, },
{
find: /^.*VPSwitchAppearance\.vue$/,
replacement: fileURLToPath(
new URL("./theme/components/CustomSwitchAppearance.vue", import.meta.url),
),
},
], ],
}, },
plugins: [ElementPlus({})], plugins: [ElementPlus({})],

View File

@ -0,0 +1,64 @@
<script setup lang="ts">
import { useData } from "vitepress"
import DefaultTheme from "vitepress/theme"
import { nextTick, provide } from "vue"
const { isDark } = useData()
function shouldEnableTransitions() {
return "startViewTransition" in document
&& window.matchMedia("(prefers-reduced-motion: no-preference)").matches
}
provide("toggle-appearance", async ({ clientX: x, clientY: y }: MouseEvent) => {
if (!shouldEnableTransitions()) {
isDark.value = !isDark.value
return
}
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)`,
]
// @ts-expect-error Missing types as its experimental
await document.startViewTransition(async () => {
isDark.value = !isDark.value
await nextTick()
}).ready
document.documentElement.animate(
{ clipPath: isDark.value ? clipPath.reverse() : clipPath },
{
duration: 300,
easing: "ease-in",
pseudoElement: `::view-transition-${isDark.value ? "old" : "new"}(root)`,
},
)
})
</script>
<template>
<DefaultTheme.Layout />
</template>
<style lang="stylus">
::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
}
</style>

View File

@ -0,0 +1,65 @@
<script setup lang="ts">
import { inject } from "vue"
import { useData } from "vitepress"
import VPIconMoon from "vitepress/dist/client/theme-default/components/icons/VPIconMoon.vue"
import VPIconSun from "vitepress/dist/client/theme-default/components/icons/VPIconSun.vue"
const { isDark } = useData()
const toggleAppearance = inject("toggle-appearance", () => {
isDark.value = !isDark.value
})
const supportsViewTransition = "startViewTransition" in document
</script>
<template>
<button
title="Toggle dark mode"
class="CustomSwitchAppearance"
:aria-checked="isDark"
:data-view-transition="supportsViewTransition"
@click="toggleAppearance"
>
<Transition name="fade" mode="out-in">
<VPIconSun v-if="!isDark" class="sun" />
<VPIconMoon v-else class="moon" />
</Transition>
</button>
</template>
<style lang="stylus" scoped>
.CustomSwitchAppearance {
display: flex
justify-content: center
align-items: center
width: 36px
height: 36px
color: var(--vp-c-text-2)
transition: color 0.5s
&:hover {
color: var(--vp-c-text-1)
transition: color 0.25s
}
& > :deep(svg) {
width: 20px
height: 20px
fill: currentColor
}
&[data-view-transition="false"] {
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.1s ease
}
.fade-enter-from,
.fade-leave-to {
opacity: 0
}
}
}
</style>

View File

@ -11,8 +11,9 @@ import { VueQueryPlugin } from "@tanstack/vue-query"
import { enhanceAppWithTabs } from "vitepress-plugin-tabs/client" import { enhanceAppWithTabs } from "vitepress-plugin-tabs/client"
// Import Icon components // Import components
import { IconBugReport, IconDownload, IconNewspaperVariant } from "@iconify-prerendered/vue-mdi" import { IconBugReport, IconDownload, IconNewspaperVariant } from "@iconify-prerendered/vue-mdi"
import Layout from "./Layout.vue"
// Import Google Analytics plugin // Import Google Analytics plugin
import googleAnalytics from "vitepress-plugin-google-analytics" import googleAnalytics from "vitepress-plugin-google-analytics"
@ -27,4 +28,5 @@ export default {
app.component("IconBugReport", IconBugReport) app.component("IconBugReport", IconBugReport)
// googleAnalytics({ id: "G-2CBXXM1Y86" }) // googleAnalytics({ id: "G-2CBXXM1Y86" })
}, },
Layout,
} }

View File

@ -230,3 +230,18 @@ main :where(h1, h2, h3, h4, h5, h6) + figure {
body { body {
--el-color-primary: var(--vp-c-brand-1) --el-color-primary: var(--vp-c-brand-1)
} }
/**
* Component: Appearance Switch
*/
.menu + .appearance::before {
margin-right: 8px !important
}
.appearance + .social-links::before {
margin-left: 8px !important
}
.VPMenu .CustomSwitchAppearance {
margin-right: -8px;
}