Skip to content

Commit

Permalink
feat: add excessive animation when switching themes (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
tolking authored Oct 13, 2024
1 parent 5522a83 commit 126cda9
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 3 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"homepage": "/~https://github.com/tolking/vitepress-theme-ououe#readme",
"lint-staged": {
"*.{ts,vue,js,tsx,jsx}": [
"prettier --write --no-verify ",
"prettier --write --no-verify",
"eslint --fix"
],
"*.{html,css,md,json}": "prettier --write"
Expand Down
95 changes: 95 additions & 0 deletions src/components/VPAppearance.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<script lang="ts" setup>
import { computed, ref, nextTick } from 'vue'
import { useData } from 'vitepress'
import VPSwitch from 'vitepress/dist/client/theme-default/components/VPSwitch.vue'
const { isDark, theme } = useData()
const switchRef = ref()
const switchTitle = computed(() => {
return isDark.value
? theme.value.lightModeSwitchTitle || 'Switch to light theme'
: theme.value.darkModeSwitchTitle || 'Switch to dark theme'
})
function toggleAppearance() {
const isAppearanceTransition =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
document.startViewTransition &&
!window.matchMedia('(prefers-reduced-motion: reduce)').matches
if (!isAppearanceTransition) {
isDark.value = !isDark.value
return
}
const switchElement = switchRef.value?.$el
const rect = switchElement.getBoundingClientRect()
const x = rect.left + rect.width / 2
const y = rect.top + rect.height / 2
const endRadius = Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y),
)
// @ts-expect-error: Transition API
const transition = document.startViewTransition(async () => {
isDark.value = !isDark.value
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: isDark.value ? [...clipPath].reverse() : clipPath,
},
{
duration: 400,
easing: 'ease-in',
pseudoElement: isDark.value
? '::view-transition-old(root)'
: '::view-transition-new(root)',
},
)
})
}
</script>

<template>
<VPSwitch
ref="switchRef"
:title="switchTitle"
:aria-checked="isDark"
class="VPSwitchAppearance"
@click="toggleAppearance"
>
<span class="vpi-sun sun" />
<span class="vpi-moon moon" />
</VPSwitch>
</template>

<style scoped>
.sun {
opacity: 1;
}
.moon {
opacity: 0;
}
.dark .sun {
opacity: 0;
}
.dark .moon {
opacity: 1;
}
.dark .VPSwitchAppearance :deep(.check) {
/*rtl:ignore*/
transform: translateX(18px);
}
</style>
18 changes: 16 additions & 2 deletions src/components/VPHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import VPImage from 'vitepress/dist/client/theme-default/components/VPImage.vue'
import VPNavBarMenu from 'vitepress/dist/client/theme-default/components/VPNavBarMenu.vue'
import VPNavBarSearch from 'vitepress/dist/client/theme-default/components/VPNavBarSearch.vue'
import VPNavBarTranslations from 'vitepress/dist/client/theme-default/components/VPNavBarTranslations.vue'
import VPNavBarAppearance from 'vitepress/dist/client/theme-default/components/VPNavBarAppearance.vue'
import VPNavBarSocialLinks from 'vitepress/dist/client/theme-default/components/VPNavBarSocialLinks.vue'
import VPNavBarHamburger from 'vitepress/dist/client/theme-default/components/VPNavBarHamburger.vue'
import VPAppearance from './VPAppearance.vue'
import type { HeaderSlots, Theme } from '../types/index'
defineProps<{
Expand Down Expand Up @@ -51,7 +51,12 @@ const homeLink = computed(() => {
<slot name="header-search-before" />
<VPNavBarSearch class="search" />
<VPNavBarTranslations class="translations" />
<VPNavBarAppearance class="appearance" />
<div
v-if="site.appearance && site.appearance !== 'force-dark'"
class="VPNavBarAppearance"
>
<VPAppearance />
</div>
<VPNavBarSocialLinks class="social-links" />
<VPNavBarHamburger
:active="isScreenOpen"
Expand Down Expand Up @@ -109,10 +114,19 @@ const homeLink = computed(() => {
.header .header-content .social-links {
margin-left: var(--vp-size-space);
}
.VPNavBarAppearance {
display: none;
margin-left: var(--vp-size-space);
}
@media (min-width: 768px) {
.header .header-content .header-logo {
flex-grow: 0;
}
.VPNavBarAppearance {
display: flex;
align-items: center;
}
}
</style>
14 changes: 14 additions & 0 deletions src/styles/public.css
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,17 @@
.scale-leave-active {
position: absolute;
}

::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: 999999999;
}

0 comments on commit 126cda9

Please sign in to comment.