mirror of
https://github.com/tachiyomiorg/website.git
synced 2024-12-20 23:22:00 +01:00
Add view transition when changing themes. (#29)
This commit is contained in:
parent
0ae836ed2e
commit
cd61ac174f
@ -55,6 +55,12 @@ export default defineConfig({
|
||||
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({})],
|
||||
|
64
website/src/.vitepress/theme/Layout.vue
Normal file
64
website/src/.vitepress/theme/Layout.vue
Normal 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>
|
@ -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>
|
@ -11,8 +11,9 @@ import { VueQueryPlugin } from "@tanstack/vue-query"
|
||||
|
||||
import { enhanceAppWithTabs } from "vitepress-plugin-tabs/client"
|
||||
|
||||
// Import Icon components
|
||||
// Import components
|
||||
import { IconBugReport, IconDownload, IconNewspaperVariant } from "@iconify-prerendered/vue-mdi"
|
||||
import Layout from "./Layout.vue"
|
||||
|
||||
// Import Google Analytics plugin
|
||||
import googleAnalytics from "vitepress-plugin-google-analytics"
|
||||
@ -27,4 +28,5 @@ export default {
|
||||
app.component("IconBugReport", IconBugReport)
|
||||
// googleAnalytics({ id: "G-2CBXXM1Y86" })
|
||||
},
|
||||
Layout,
|
||||
}
|
||||
|
@ -230,3 +230,18 @@ main :where(h1, h2, h3, h4, h5, h6) + figure {
|
||||
body {
|
||||
--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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user