mirror of
https://github.com/tachiyomiorg/website.git
synced 2024-12-21 07:31:58 +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),
|
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({})],
|
||||||
|
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 { 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,
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user