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;
|
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-old(root),
|
||||||
::view-transition-new(root) {
|
::view-transition-new(root) {
|
||||||
animation: none;
|
animation: none;
|
||||||
mix-blend-mode: normal;
|
mix-blend-mode: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 切换浅色(变白):新视图在顶层,从点击位置展开 */
|
||||||
::view-transition-old(root) {
|
::view-transition-old(root) {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
::view-transition-new(root) {
|
::view-transition-new(root) {
|
||||||
z-index: 2147483646;
|
z-index: 2147483646;
|
||||||
|
animation: switch-to-light 400ms ease-in-out forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 切换深色(变黑):旧视图在顶层,向点击位置收缩 */
|
||||||
.dark::view-transition-old(root) {
|
.dark::view-transition-old(root) {
|
||||||
z-index: 2147483646;
|
z-index: 2147483646;
|
||||||
|
animation: switch-to-dark 400ms ease-in-out forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark::view-transition-new(root) {
|
.dark::view-transition-new(root) {
|
||||||
z-index: 1;
|
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 globalConfigStore = useGlobalConfigStore()
|
||||||
|
|
||||||
const toggleTheme = (event) => {
|
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 mouseEvent = event && typeof event.clientX === 'number' ? event : window.event
|
||||||
const x = mouseEvent?.clientX ?? window.innerWidth / 2
|
const targetEl = mouseEvent?.target || document.querySelector('.icon-moon, .icon-sunny')?.closest('.el-menu-item, button, .el-dropdown-link')
|
||||||
const y = mouseEvent?.clientY ?? window.innerHeight / 2
|
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(
|
const endRadius = Math.hypot(
|
||||||
Math.max(x, innerWidth - x),
|
Math.max(x, window.innerWidth - x),
|
||||||
Math.max(y, innerHeight - y)
|
Math.max(y, window.innerHeight - y)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fallback for browsers without View Transition API
|
// Fallback for browsers without View Transition API
|
||||||
@@ -155,46 +155,14 @@ export const useThemeAndLocaleMenus = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable transitions to prevent "fading" snapshot
|
document.documentElement.style.setProperty('--theme-x', `${x}px`)
|
||||||
const css = document.createElement('style')
|
document.documentElement.style.setProperty('--theme-y', `${y}px`)
|
||||||
css.appendChild(document.createTextNode(`* {
|
document.documentElement.style.setProperty('--theme-r', `${endRadius}px`)
|
||||||
-webkit-transition: none !important;
|
|
||||||
-moz-transition: none !important;
|
|
||||||
-o-transition: none !important;
|
|
||||||
-ms-transition: none !important;
|
|
||||||
transition: none !important;
|
|
||||||
}`))
|
|
||||||
document.head.appendChild(css)
|
|
||||||
|
|
||||||
const transition = document.startViewTransition(async () => {
|
document.startViewTransition(async () => {
|
||||||
globalConfigStore.changeTheme(!globalConfigStore.isDarkTheme)
|
globalConfigStore.changeTheme(!globalConfigStore.isDarkTheme)
|
||||||
await nextTick()
|
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 [{
|
return [{
|
||||||
|
|||||||
Reference in New Issue
Block a user