mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-09-27 05:36:43 +08:00
feat(components): add theme switching animation
This commit is contained in:
parent
a9dce21878
commit
4a2dbbf1d9
@ -1,13 +1,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
import { computed, nextTick } from 'vue';
|
||||||
import type { PopoverPlacement } from 'naive-ui';
|
import type { PopoverPlacement } from 'naive-ui';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
|
import { useThemeStore } from '@/store/modules/theme';
|
||||||
|
|
||||||
defineOptions({ name: 'ThemeSchemaSwitch' });
|
defineOptions({ name: 'ThemeSchemaSwitch' });
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
/** Theme schema */
|
|
||||||
themeSchema: UnionKey.ThemeScheme;
|
|
||||||
/** Show tooltip */
|
/** Show tooltip */
|
||||||
showTooltip?: boolean;
|
showTooltip?: boolean;
|
||||||
/** Tooltip placement */
|
/** Tooltip placement */
|
||||||
@ -19,15 +18,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
tooltipPlacement: 'bottom'
|
tooltipPlacement: 'bottom'
|
||||||
});
|
});
|
||||||
|
|
||||||
interface Emits {
|
const themeStore = useThemeStore();
|
||||||
(e: 'switch'): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits<Emits>();
|
|
||||||
|
|
||||||
function handleSwitch() {
|
|
||||||
emit('switch');
|
|
||||||
}
|
|
||||||
|
|
||||||
const icons: Record<UnionKey.ThemeScheme, string> = {
|
const icons: Record<UnionKey.ThemeScheme, string> = {
|
||||||
light: 'material-symbols:sunny',
|
light: 'material-symbols:sunny',
|
||||||
@ -35,13 +26,46 @@ const icons: Record<UnionKey.ThemeScheme, string> = {
|
|||||||
auto: 'material-symbols:hdr-auto'
|
auto: 'material-symbols:hdr-auto'
|
||||||
};
|
};
|
||||||
|
|
||||||
const icon = computed(() => icons[props.themeSchema]);
|
const icon = computed(() => icons[themeStore.themeScheme]);
|
||||||
|
|
||||||
const tooltipContent = computed(() => {
|
const tooltipContent = computed(() => {
|
||||||
if (!props.showTooltip) return '';
|
if (!props.showTooltip) return '';
|
||||||
|
|
||||||
return $t('icon.themeSchema');
|
return $t('icon.themeSchema');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function toggleDark(event: MouseEvent) {
|
||||||
|
const isAppearanceTransition =
|
||||||
|
document.startViewTransition && !window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||||
|
|
||||||
|
if (!isAppearanceTransition) {
|
||||||
|
themeStore.toggleThemeScheme();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const x = event.clientX;
|
||||||
|
const y = event.clientY;
|
||||||
|
const endRadius = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y));
|
||||||
|
// @ts-expect-error: Transition API
|
||||||
|
const transition = document.startViewTransition(async () => {
|
||||||
|
themeStore.toggleThemeScheme();
|
||||||
|
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: themeStore.darkMode ? [...clipPath].reverse() : clipPath
|
||||||
|
},
|
||||||
|
{
|
||||||
|
duration: 400,
|
||||||
|
easing: 'ease-out',
|
||||||
|
pseudoElement: themeStore.darkMode ? '::view-transition-old(root)' : '::view-transition-new(root)'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -49,7 +73,7 @@ const tooltipContent = computed(() => {
|
|||||||
:icon="icon"
|
:icon="icon"
|
||||||
:tooltip-content="tooltipContent"
|
:tooltip-content="tooltipContent"
|
||||||
:tooltip-placement="tooltipPlacement"
|
:tooltip-placement="tooltipPlacement"
|
||||||
@click="handleSwitch"
|
@click="toggleDark"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -41,11 +41,7 @@ const { isFullscreen, toggle } = useFullscreen();
|
|||||||
<GlobalSearch />
|
<GlobalSearch />
|
||||||
<FullScreen v-if="!appStore.isMobile" :full="isFullscreen" @click="toggle" />
|
<FullScreen v-if="!appStore.isMobile" :full="isFullscreen" @click="toggle" />
|
||||||
<LangSwitch :lang="appStore.locale" :lang-options="appStore.localeOptions" @change-lang="appStore.changeLocale" />
|
<LangSwitch :lang="appStore.locale" :lang-options="appStore.localeOptions" @change-lang="appStore.changeLocale" />
|
||||||
<ThemeSchemaSwitch
|
<ThemeSchemaSwitch />
|
||||||
:theme-schema="themeStore.themeScheme"
|
|
||||||
:is-dark="themeStore.darkMode"
|
|
||||||
@switch="themeStore.toggleThemeScheme"
|
|
||||||
/>
|
|
||||||
<ThemeButton />
|
<ThemeButton />
|
||||||
<UserAvatar />
|
<UserAvatar />
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,3 +15,21 @@ html {
|
|||||||
html.grayscale {
|
html.grayscale {
|
||||||
filter: grayscale(100%);
|
filter: grayscale(100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::view-transition-old(root),
|
||||||
|
::view-transition-new(root) {
|
||||||
|
animation: none;
|
||||||
|
mix-blend-mode: normal;
|
||||||
|
}
|
||||||
|
::view-transition-old(root) {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
::view-transition-new(root) {
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
.dark::view-transition-old(root) {
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
.dark::view-transition-new(root) {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
@ -59,12 +59,7 @@ const bgColor = computed(() => {
|
|||||||
<SystemLogo class="text-64px text-primary lt-sm:text-48px" />
|
<SystemLogo class="text-64px text-primary lt-sm:text-48px" />
|
||||||
<h3 class="text-28px text-primary font-500 lt-sm:text-22px">{{ $t('system.title') }}</h3>
|
<h3 class="text-28px text-primary font-500 lt-sm:text-22px">{{ $t('system.title') }}</h3>
|
||||||
<div class="i-flex-col">
|
<div class="i-flex-col">
|
||||||
<ThemeSchemaSwitch
|
<ThemeSchemaSwitch :show-tooltip="false" class="text-20px lt-sm:text-18px" />
|
||||||
:theme-schema="themeStore.themeScheme"
|
|
||||||
:show-tooltip="false"
|
|
||||||
class="text-20px lt-sm:text-18px"
|
|
||||||
@switch="themeStore.toggleThemeScheme"
|
|
||||||
/>
|
|
||||||
<LangSwitch
|
<LangSwitch
|
||||||
:lang="appStore.locale"
|
:lang="appStore.locale"
|
||||||
:lang-options="appStore.localeOptions"
|
:lang-options="appStore.localeOptions"
|
||||||
|
Loading…
Reference in New Issue
Block a user