From eb3087edf4d4f09d2a8a72f0b01b2f95213a17df Mon Sep 17 00:00:00 2001 From: xfy Date: Fri, 3 Jul 2026 13:26:40 +0800 Subject: [PATCH] =?UTF-8?q?fix(theme):=20=E8=B7=9F=E9=9A=8F=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E5=88=87=E6=8D=A2=E7=94=A8=E4=B8=BB=E9=A2=98=E6=8C=89?= =?UTF-8?q?=E9=92=AE=E4=BD=8D=E7=BD=AE=E4=BD=9C=E4=B8=BA=20VT=20=E5=9C=86?= =?UTF-8?q?=E5=BF=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 诊断发现跟随系统与手动点击的 JS 路径完全一致(__startViewTransition 全程 正常,无降级、无 reject),「看起来像淡入淡出」实为圆形展开动画在屏幕中心 圆心下的必然视觉效果:从中心向四周等距扩散,各方向同步推进,近似整体覆盖, 与手动点击(按钮在右上角,圆弧明显横扫)观感差异大。 改用 .theme-toggle 按钮的 getBoundingClientRect() 中心作为 VT 圆心,与手动 点击的动画起点一致;按钮不在 DOM 时回退到视口中心。同步更新注释说明。 --- src/theme.rs | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/src/theme.rs b/src/theme.rs index 6d21b01..72abae2 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -206,8 +206,9 @@ pub fn use_theme_provider() -> Signal { // // 仅在 System 模式(theme == System)且 resolved 实际翻转时触发:Light/Dark 显式 // 模式下 resolved 由 theme 决定,DOM 已由 ThemeToggle::onclick 全权处理,effect - // 不介入,避免与手动点击的 VT 动画重复触发。以视口中心为圆心复用同一条 JS 路径 - // (__startThemeTransition 从 DOM 现状推导目标明暗)。 + // 不介入,避免与手动点击的 VT 动画重复触发。以主题按钮位置为圆心复用同一条 JS + // 路径(__startThemeTransition 从 DOM 现状推导目标明暗),让自动切换与手动点击 + // 的动画起点一致;屏幕中心圆心会让圆形展开近似「整体覆盖」,被误看成淡入淡出。 // // 首次挂载自然跳过:effect 通过 prev 信号记录上一次 resolved,初次运行 prev 为 // None,此时 DOM 已由 ThemePreload 脚本设为正确状态,直接对齐 prev 后返回。 @@ -230,26 +231,38 @@ pub fn use_theme_provider() -> Signal { return; } prev_resolved.set(Some(current)); - // resolved 翻转 → 以视口中心为圆心触发 VT 圆形展开动画。 + // resolved 翻转 → 触发 VT 圆形展开动画。 let Some(window) = web_sys::window() else { return; }; - let x = window - .inner_width() - .ok() - .and_then(|v| v.as_f64()) - .map(|w| w / 2.0) - .unwrap_or(0.0); - let y = window - .inner_height() - .ok() - .and_then(|v| v.as_f64()) - .map(|h| h / 2.0) - .unwrap_or(0.0); + // 圆心优先取主题切换按钮(.theme-toggle)的中心,与手动点击的动画起点一致; + // 按钮不在 DOM(如未渲染导航栏的页面)时回退到视口中心。 + use wasm_bindgen::JsCast; + let (x, y) = window + .document() + .and_then(|doc| doc.query_selector(".theme-toggle").ok().flatten()) + .map(|el| { + let r = el.unchecked_into::().get_bounding_client_rect(); + (r.x() + r.width() / 2.0, r.y() + r.height() / 2.0) + }) + .unwrap_or_else(|| { + let w = window + .inner_width() + .ok() + .and_then(|v| v.as_f64()) + .map(|v| v / 2.0) + .unwrap_or(0.0); + let h = window + .inner_height() + .ok() + .and_then(|v| v.as_f64()) + .map(|v| v / 2.0) + .unwrap_or(0.0); + (w, h) + }); let key = "__startThemeTransition".into(); if let Ok(fn_val) = js_sys::Reflect::get(&window, &key) { if !fn_val.is_undefined() && !fn_val.is_null() { - use wasm_bindgen::JsCast; let fn_obj = fn_val.unchecked_into::(); let _ = fn_obj.call2(&window, &x.into(), &y.into()); }