diff --git a/libs/yggdrasil-core/src/style.css b/libs/yggdrasil-core/src/style.css index 0726010..49d65b8 100644 --- a/libs/yggdrasil-core/src/style.css +++ b/libs/yggdrasil-core/src/style.css @@ -1,11 +1,14 @@ /* ========== 主题切换圆形展开(View Transitions API) ========== */ -/* mix-blend-mode 不可通过 Web Animations API 设置,只能用 CSS。 - UA 默认 plus-lighter 会导致新旧层颜色叠加发亮,需覆盖为 normal。 - author 样式天然覆盖 UA 样式,无需 !important。 */ +/* 硬锁 VT 伪元素样式:禁用 UA 默认的 fade-in/out, + 强制两层都保持完全不透明 + 正常混合模式。 + WAAPI 仅控制 ::view-transition-new 的 clip-path 展开,不涉及 opacity。 + !important 确保覆盖 UA origin。 */ ::view-transition-old(root), ::view-transition-new(root) { - mix-blend-mode: normal; + animation: none !important; + opacity: 1 !important; + mix-blend-mode: normal !important; } /* 在 View Transition 期间强制禁用所有 CSS 过渡, diff --git a/libs/yggdrasil-core/src/theme-transition.test.ts b/libs/yggdrasil-core/src/theme-transition.test.ts index f9e1561..ad33ce1 100644 --- a/libs/yggdrasil-core/src/theme-transition.test.ts +++ b/libs/yggdrasil-core/src/theme-transition.test.ts @@ -71,11 +71,11 @@ describe('startThemeTransition', () => { // ready 后通过 Web Animations API 控制动画 await readyP; await Promise.resolve(); - // animate 应被调用两次:一次 old(opacity),一次 new(clipPath + opacity) - expect(animateSpy).toHaveBeenCalledTimes(2); - // 第二次调用(new)应包含 clipPath + // animate 应被调用一次:clip-path 展开 on new + expect(animateSpy).toHaveBeenCalledTimes(1); + // 调用应包含 clipPath + pseudoElement const calls = animateSpy.mock.calls as unknown[][]; - const newCall = calls[1]; + const newCall = calls[0]; expect(newCall[0]).toHaveProperty('clipPath'); expect(newCall[1]).toHaveProperty('pseudoElement', '::view-transition-new(root)'); diff --git a/libs/yggdrasil-core/src/theme-transition.ts b/libs/yggdrasil-core/src/theme-transition.ts index 5e15fe5..18fa6d2 100644 --- a/libs/yggdrasil-core/src/theme-transition.ts +++ b/libs/yggdrasil-core/src/theme-transition.ts @@ -82,28 +82,18 @@ export function startThemeTransition(x: number, y: number): void { vt.ready .then(() => { - console.log('[tt] ready, animating via WAAPI'); - // ── Web Animations API ── - // script-created 动画优先级高于 CSS 动画(含 UA 默认 fade-in/out), - // 天然覆盖,无需 !important,无残留样式问题。 - - // OLD: 覆盖 UA 默认的 fade-out,保持完全可见作为底图 - document.documentElement.animate( - { opacity: [1, 1] }, - { - duration: 400, - pseudoElement: '::view-transition-old(root)', - }, - ); - - // NEW: 覆盖 UA 默认的 fade-in,改为 clip-path 圆形展开 + console.log('[tt] ready, animating clip-path via WAAPI'); + // CSS 已通过 animation:none + opacity:1 (!important) 锁定两层: + // OLD 保持完全可见(暗色底图),NEW 保持完全不透明。 + // WAAPI 只需控制 NEW 的 clip-path 实现圆形展开。 + // script-created Animation 优先级高于 CSS animation(已被 none 禁用), + // 只添加 clip-path 动画,不与 CSS 冲突。 document.documentElement.animate( { clipPath: [ `circle(0px at ${x}px ${y}px)`, `circle(${maxR}px at ${x}px ${y}px)`, ], - opacity: [1, 1], }, { duration: 400,