enhance(frontend): テーマ切り替えのアニメーションをView Transitionに変更
This commit is contained in:
		
							parent
							
								
									cdd131d542
								
							
						
					
					
						commit
						f28d30cfc9
					
				|  | @ -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; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| html._themeChanging_ { | ||||
| 	view-transition-name: theme-changing; | ||||
| } | ||||
| 
 | ||||
| html::view-transition-new(theme-changing) { | ||||
| 	z-index: 4000001; | ||||
| 	animation: themeChangingNew 1s ease; | ||||
| 	animation-fill-mode: forwards; | ||||
| } | ||||
| 
 | ||||
| html::view-transition-old(theme-changing) { | ||||
| 	z-index: 4000000; | ||||
| 	animation: themeChangingOld 1s 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,38 @@ export function applyTheme(theme: Theme, persist = true) { | |||
| 	globalEvents.emit('themeChanging'); | ||||
| } | ||||
| 
 | ||||
| let timeout: number | null = null; | ||||
| 
 | ||||
| export function applyTheme(theme: Theme, persist = true) { | ||||
| 	if (timeout) { | ||||
| 		clearTimeout(timeout); | ||||
| 		timeout = null; | ||||
| 	} | ||||
| 
 | ||||
| 	if (document.startViewTransition != null && prefer.s.animation) { | ||||
| 		window.document.documentElement.classList.add('_themeChanging_'); | ||||
| 		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'); | ||||
| 		}, 1000); | ||||
| 
 | ||||
| 		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