mirror of
https://github.com/fugary/simple-element-plus-template.git
synced 2026-09-11 09:23:53 +00:00
opt: 优化主题切换动画效果,防止出现动画断层
This commit is contained in:
@@ -546,27 +546,56 @@ body,
|
||||
position: unset;
|
||||
}
|
||||
|
||||
/* Theme transition animation (from Element Plus) */
|
||||
/* Theme transition animation */
|
||||
:root {
|
||||
--theme-x: 50%;
|
||||
--theme-y: 50%;
|
||||
--theme-r: 1500px;
|
||||
}
|
||||
|
||||
::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: 2147483646;
|
||||
animation: switch-to-light 400ms ease-in-out forwards;
|
||||
}
|
||||
|
||||
/* 切换深色(变黑):旧视图在顶层,向点击位置收缩 */
|
||||
.dark::view-transition-old(root) {
|
||||
z-index: 2147483646;
|
||||
animation: switch-to-dark 400ms ease-in-out forwards;
|
||||
}
|
||||
|
||||
.dark::view-transition-new(root) {
|
||||
z-index: 1;
|
||||
animation: none;
|
||||
}
|
||||
|
||||
@keyframes switch-to-light {
|
||||
from {
|
||||
clip-path: circle(0px at var(--theme-x) var(--theme-y));
|
||||
}
|
||||
to {
|
||||
clip-path: circle(var(--theme-r) at var(--theme-x) var(--theme-y));
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes switch-to-dark {
|
||||
from {
|
||||
clip-path: circle(var(--theme-r) at var(--theme-x) var(--theme-y));
|
||||
}
|
||||
to {
|
||||
clip-path: circle(0px at var(--theme-x) var(--theme-y));
|
||||
}
|
||||
}
|
||||
|
||||
/* 整体编辑器区域 */
|
||||
|
||||
@@ -138,15 +138,15 @@ export const useThemeAndLocaleMenus = () => {
|
||||
const globalConfigStore = useGlobalConfigStore()
|
||||
|
||||
const toggleTheme = (event) => {
|
||||
// Attempt to get coordinates from the event or global event
|
||||
// Element Plus el-menu-item emits a custom object, not a MouseEvent, so event.clientX might be missing.
|
||||
const mouseEvent = event && typeof event.clientX === 'number' ? event : window.event
|
||||
const x = mouseEvent?.clientX ?? window.innerWidth / 2
|
||||
const y = mouseEvent?.clientY ?? window.innerHeight / 2
|
||||
const targetEl = mouseEvent?.target || document.querySelector('.icon-moon, .icon-sunny')?.closest('.el-menu-item, button, .el-dropdown-link')
|
||||
const rect = targetEl?.getBoundingClientRect?.()
|
||||
const x = mouseEvent?.clientX ?? (rect ? Math.round(rect.left + rect.width / 2) : window.innerWidth - 80)
|
||||
const y = mouseEvent?.clientY ?? (rect ? Math.round(rect.top + rect.height / 2) : 30)
|
||||
|
||||
const endRadius = Math.hypot(
|
||||
Math.max(x, innerWidth - x),
|
||||
Math.max(y, innerHeight - y)
|
||||
Math.max(x, window.innerWidth - x),
|
||||
Math.max(y, window.innerHeight - y)
|
||||
)
|
||||
|
||||
// Fallback for browsers without View Transition API
|
||||
@@ -155,46 +155,14 @@ export const useThemeAndLocaleMenus = () => {
|
||||
return
|
||||
}
|
||||
|
||||
// Disable transitions to prevent "fading" snapshot
|
||||
const css = document.createElement('style')
|
||||
css.appendChild(document.createTextNode(`* {
|
||||
-webkit-transition: none !important;
|
||||
-moz-transition: none !important;
|
||||
-o-transition: none !important;
|
||||
-ms-transition: none !important;
|
||||
transition: none !important;
|
||||
}`))
|
||||
document.head.appendChild(css)
|
||||
document.documentElement.style.setProperty('--theme-x', `${x}px`)
|
||||
document.documentElement.style.setProperty('--theme-y', `${y}px`)
|
||||
document.documentElement.style.setProperty('--theme-r', `${endRadius}px`)
|
||||
|
||||
const transition = document.startViewTransition(async () => {
|
||||
document.startViewTransition(async () => {
|
||||
globalConfigStore.changeTheme(!globalConfigStore.isDarkTheme)
|
||||
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: globalConfigStore.isDarkTheme ? [...clipPath].reverse() : clipPath
|
||||
},
|
||||
{
|
||||
duration: 400,
|
||||
easing: 'ease-in',
|
||||
fill: 'forwards',
|
||||
pseudoElement: globalConfigStore.isDarkTheme
|
||||
? '::view-transition-old(root)'
|
||||
: '::view-transition-new(root)'
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
// Clean up the transition disable style after the animation finishes
|
||||
transition.finished.then(() => {
|
||||
document.head.removeChild(css)
|
||||
})
|
||||
}
|
||||
|
||||
return [{
|
||||
|
||||
Reference in New Issue
Block a user