diff options
| author | かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> | 2025-05-21 14:19:34 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-05-21 14:19:34 +0900 |
| commit | 2619f69238034dbd80942a26550ef25b903bc1dc (patch) | |
| tree | d4e107ecd0acac194aeb6fa54072e746a54a6674 /packages/frontend/src | |
| parent | enhance(frontend/aiscript): AiScriptからtoastを発行できるように (#... (diff) | |
| download | misskey-2619f69238034dbd80942a26550ef25b903bc1dc.tar.gz misskey-2619f69238034dbd80942a26550ef25b903bc1dc.tar.bz2 misskey-2619f69238034dbd80942a26550ef25b903bc1dc.zip | |
enhance(frontend): テーマ切り替えのアニメーションをView Transitionに変更 (#15974)
* enhance(frontend): テーマ切り替えのアニメーションをView Transitionに変更
* fix lint
* fix: 切り替え時間を0.5sに
Diffstat (limited to 'packages/frontend/src')
| -rw-r--r-- | packages/frontend/src/components/MkFoldableSection.vue | 16 | ||||
| -rw-r--r-- | packages/frontend/src/style.scss | 41 | ||||
| -rw-r--r-- | packages/frontend/src/theme.ts | 48 |
3 files changed, 86 insertions, 19 deletions
diff --git a/packages/frontend/src/components/MkFoldableSection.vue b/packages/frontend/src/components/MkFoldableSection.vue index b9888d9b64..0fa7bea7ab 100644 --- a/packages/frontend/src/components/MkFoldableSection.vue +++ b/packages/frontend/src/components/MkFoldableSection.vue @@ -31,9 +31,10 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, ref, useTemplateRef, watch } from 'vue'; +import { onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from 'vue'; import { miLocalStorage } from '@/local-storage.js'; import { prefer } from '@/preferences.js'; +import { globalEvents } from '@/events.js'; import { getBgColor } from '@/utility/get-bg-color.js'; const miLocalStoragePrefix = 'ui:folder:' as const; @@ -83,8 +84,19 @@ function afterLeave(el: Element) { el.style.height = ''; } +function updateBgColor() { + if (rootEl.value) { + parentBg.value = getBgColor(rootEl.value.parentElement); + } +} + onMounted(() => { - parentBg.value = getBgColor(rootEl.value?.parentElement); + updateBgColor(); + globalEvents.on('themeChanging', updateBgColor); +}); + +onBeforeUnmount(() => { + globalEvents.off('themeChanging', updateBgColor); }); </script> diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss index 30936fbb10..12879e63b3 100644 --- a/packages/frontend/src/style.scss +++ b/packages/frontend/src/style.scss @@ -90,12 +90,49 @@ html { } } -html._themeChanging_ { +html._themeChangingFallback_ { &, * { - transition: background 1s ease, border 1s ease !important; + transition: background 0.5s ease, border 0.5s ease !important; } } +html._themeChanging_ { + view-transition-name: theme-changing; +} + +html::view-transition-new(theme-changing) { + z-index: 4000001; + animation: themeChangingNew 0.5s ease; + animation-fill-mode: forwards; +} + +html::view-transition-old(theme-changing) { + z-index: 4000000; + animation: themeChangingOld 0.5s ease; + animation-fill-mode: forwards; +} + +@keyframes themeChangingNew { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +@keyframes themeChangingOld { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + } + +} + html, body, #misskey_app { diff --git a/packages/frontend/src/theme.ts b/packages/frontend/src/theme.ts index 268f879d17..9562f69cf3 100644 --- a/packages/frontend/src/theme.ts +++ b/packages/frontend/src/theme.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { ref } from 'vue'; +import { ref, nextTick } from 'vue'; import tinycolor from 'tinycolor2'; import lightTheme from '@@/themes/_light.json5'; import darkTheme from '@@/themes/_dark.json5'; @@ -88,20 +88,7 @@ export async function removeTheme(theme: Theme): Promise<void> { prefer.commit('themes', themes); } -let timeout: number | null = null; - -export function applyTheme(theme: Theme, persist = true) { - if (timeout) window.clearTimeout(timeout); - - window.document.documentElement.classList.add('_themeChanging_'); - - timeout = window.setTimeout(() => { - window.document.documentElement.classList.remove('_themeChanging_'); - - // 色計算など再度行えるようにクライアント全体に通知 - globalEvents.emit('themeChanged'); - }, 1000); - +function applyThemeInternal(theme: Theme, persist: boolean) { const colorScheme = theme.base === 'dark' ? 'dark' : 'light'; window.document.documentElement.dataset.colorScheme = colorScheme; @@ -139,6 +126,37 @@ export function applyTheme(theme: Theme, persist = true) { globalEvents.emit('themeChanging'); } +let timeout: number | null = null; + +export function applyTheme(theme: Theme, persist = true) { + if (timeout) { + window.clearTimeout(timeout); + timeout = null; + } + + if (window.document.startViewTransition != null && prefer.s.animation) { + window.document.documentElement.classList.add('_themeChanging_'); + window.document.startViewTransition(async () => { + applyThemeInternal(theme, persist); + await nextTick(); + }).finished.then(() => { + window.document.documentElement.classList.remove('_themeChanging_'); + // 色計算など再度行えるようにクライアント全体に通知 + globalEvents.emit('themeChanged'); + }); + } else { + // TODO: ViewTransition API が主要ブラウザで対応したら消す + window.document.documentElement.classList.add('_themeChangingFallback_'); + timeout = window.setTimeout(() => { + window.document.documentElement.classList.remove('_themeChangingFallback_'); + // 色計算など再度行えるようにクライアント全体に通知 + globalEvents.emit('themeChanged'); + }, 500); + + applyThemeInternal(theme, persist); + } +} + export function compile(theme: Theme): Record<string, string> { function getColor(val: string): tinycolor.Instance { if (val[0] === '@') { // ref (prop) |