fix(theme): 跟随系统切换用主题按钮位置作为 VT 圆心
诊断发现跟随系统与手动点击的 JS 路径完全一致(__startViewTransition 全程 正常,无降级、无 reject),「看起来像淡入淡出」实为圆形展开动画在屏幕中心 圆心下的必然视觉效果:从中心向四周等距扩散,各方向同步推进,近似整体覆盖, 与手动点击(按钮在右上角,圆弧明显横扫)观感差异大。 改用 .theme-toggle 按钮的 getBoundingClientRect() 中心作为 VT 圆心,与手动 点击的动画起点一致;按钮不在 DOM 时回退到视口中心。同步更新注释说明。
This commit is contained in:
parent
740afca05a
commit
eb3087edf4
45
src/theme.rs
45
src/theme.rs
@ -206,8 +206,9 @@ pub fn use_theme_provider() -> Signal<Theme> {
|
|||||||
//
|
//
|
||||||
// 仅在 System 模式(theme == System)且 resolved 实际翻转时触发:Light/Dark 显式
|
// 仅在 System 模式(theme == System)且 resolved 实际翻转时触发:Light/Dark 显式
|
||||||
// 模式下 resolved 由 theme 决定,DOM 已由 ThemeToggle::onclick 全权处理,effect
|
// 模式下 resolved 由 theme 决定,DOM 已由 ThemeToggle::onclick 全权处理,effect
|
||||||
// 不介入,避免与手动点击的 VT 动画重复触发。以视口中心为圆心复用同一条 JS 路径
|
// 不介入,避免与手动点击的 VT 动画重复触发。以主题按钮位置为圆心复用同一条 JS
|
||||||
// (__startThemeTransition 从 DOM 现状推导目标明暗)。
|
// 路径(__startThemeTransition 从 DOM 现状推导目标明暗),让自动切换与手动点击
|
||||||
|
// 的动画起点一致;屏幕中心圆心会让圆形展开近似「整体覆盖」,被误看成淡入淡出。
|
||||||
//
|
//
|
||||||
// 首次挂载自然跳过:effect 通过 prev 信号记录上一次 resolved,初次运行 prev 为
|
// 首次挂载自然跳过:effect 通过 prev 信号记录上一次 resolved,初次运行 prev 为
|
||||||
// None,此时 DOM 已由 ThemePreload 脚本设为正确状态,直接对齐 prev 后返回。
|
// None,此时 DOM 已由 ThemePreload 脚本设为正确状态,直接对齐 prev 后返回。
|
||||||
@ -230,26 +231,38 @@ pub fn use_theme_provider() -> Signal<Theme> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
prev_resolved.set(Some(current));
|
prev_resolved.set(Some(current));
|
||||||
// resolved 翻转 → 以视口中心为圆心触发 VT 圆形展开动画。
|
// resolved 翻转 → 触发 VT 圆形展开动画。
|
||||||
let Some(window) = web_sys::window() else {
|
let Some(window) = web_sys::window() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let x = window
|
// 圆心优先取主题切换按钮(.theme-toggle)的中心,与手动点击的动画起点一致;
|
||||||
.inner_width()
|
// 按钮不在 DOM(如未渲染导航栏的页面)时回退到视口中心。
|
||||||
.ok()
|
use wasm_bindgen::JsCast;
|
||||||
.and_then(|v| v.as_f64())
|
let (x, y) = window
|
||||||
.map(|w| w / 2.0)
|
.document()
|
||||||
.unwrap_or(0.0);
|
.and_then(|doc| doc.query_selector(".theme-toggle").ok().flatten())
|
||||||
let y = window
|
.map(|el| {
|
||||||
.inner_height()
|
let r = el.unchecked_into::<web_sys::Element>().get_bounding_client_rect();
|
||||||
.ok()
|
(r.x() + r.width() / 2.0, r.y() + r.height() / 2.0)
|
||||||
.and_then(|v| v.as_f64())
|
})
|
||||||
.map(|h| h / 2.0)
|
.unwrap_or_else(|| {
|
||||||
.unwrap_or(0.0);
|
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();
|
let key = "__startThemeTransition".into();
|
||||||
if let Ok(fn_val) = js_sys::Reflect::get(&window, &key) {
|
if let Ok(fn_val) = js_sys::Reflect::get(&window, &key) {
|
||||||
if !fn_val.is_undefined() && !fn_val.is_null() {
|
if !fn_val.is_undefined() && !fn_val.is_null() {
|
||||||
use wasm_bindgen::JsCast;
|
|
||||||
let fn_obj = fn_val.unchecked_into::<js_sys::Function>();
|
let fn_obj = fn_val.unchecked_into::<js_sys::Function>();
|
||||||
let _ = fn_obj.call2(&window, &x.into(), &y.into());
|
let _ = fn_obj.call2(&window, &x.into(), &y.into());
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user