enhance(frontend): テーマ切り替えのアニメーションをView Transitionに変更 (#15974)
* enhance(frontend): テーマ切り替えのアニメーションをView Transitionに変更 * fix lint * fix: 切り替え時間を0.5sに
This commit is contained in:
parent
bd7633c70e
commit
2619f69238
|
@ -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>
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue