diff --git a/src/components/admin_layout.rs b/src/components/admin_layout.rs index 02e9d34..75d05bd 100644 --- a/src/components/admin_layout.rs +++ b/src/components/admin_layout.rs @@ -8,9 +8,9 @@ use dioxus::router::components::Link; use crate::api::auth::{get_current_user, logout}; use crate::components::admin_skeleton::AdminDashboardSkeleton; +use crate::components::skeletons::delayed_skeleton::DelayedSkeleton; use crate::components::write_skeleton::WriteSkeleton; use crate::context::UserContext; -use crate::hooks::delayed_loading::use_delayed_loading; use crate::router::Route; use crate::theme::ThemeToggle; @@ -19,7 +19,7 @@ pub fn AdminLayout() -> Element { let mut ctx: UserContext = use_context(); let navigator = dioxus::router::navigator(); let route = use_route::(); - let show_skeleton = use_delayed_loading(move || !(ctx.checked)()); + use_effect(move || { if !(ctx.checked)() { @@ -145,11 +145,13 @@ pub fn AdminLayout() -> Element { div { class: "flex-1 flex flex-col min-w-0 h-screen p-2 md:p-4", div { class: "flex-1 bg-[var(--color-paper-theme)] rounded-[2rem] shadow-sm border border-[var(--color-paper-border)] overflow-hidden relative flex flex-col", main { class: "{main_class}", - div { class: if show_skeleton() { "p-10" } else { "opacity-0" }, - { - match route { - Route::Write {} => rsx! { WriteSkeleton {} }, - _ => rsx! { AdminDashboardSkeleton {} }, + DelayedSkeleton { + div { class: "p-10", + { + match route { + Route::Write {} => rsx! { WriteSkeleton {} }, + _ => rsx! { AdminDashboardSkeleton {} }, + } } } } diff --git a/src/components/comments/section.rs b/src/components/comments/section.rs index 67cd609..c3f543d 100644 --- a/src/components/comments/section.rs +++ b/src/components/comments/section.rs @@ -9,6 +9,7 @@ use crate::api::comments::{check_pending_status, get_comments, CommentTreeRespon use crate::components::comments::form::CommentForm; use crate::components::comments::list::CommentList; use crate::components::skeletons::comment_skeleton::CommentListSkeleton; +use crate::components::skeletons::delayed_skeleton::DelayedSkeleton; use crate::utils::comment_storage::{self, PendingComment}; /// 评论上下文,供评论相关组件共享状态。 @@ -118,7 +119,7 @@ pub fn CommentSection(post_id: i32) -> Element { } } None => rsx! { - CommentListSkeleton {} + DelayedSkeleton { CommentListSkeleton {} } }, } } diff --git a/src/components/skeletons/delayed_skeleton.rs b/src/components/skeletons/delayed_skeleton.rs index 775d45c..74e5de0 100644 --- a/src/components/skeletons/delayed_skeleton.rs +++ b/src/components/skeletons/delayed_skeleton.rs @@ -1,36 +1,39 @@ //! 延迟骨架屏包装组件 //! -//! 让骨架屏先以静态灰色块立即显示,延迟一段时间后再启动 pulse 动画, -//! 避免快速加载时动画一闪而过带来的视觉抖动。 +//! 200ms 内完全不渲染骨架屏,避免快速加载时骨架屏一闪而过; +//! 超过 200ms 后渲染骨架屏并带有 pulse 动画,提示正在加载。 use crate::utils::time::sleep_ms; use dioxus::prelude::*; -/// 骨架屏 pulse 动画延迟(毫秒)。 +/// 骨架屏显示延迟(毫秒)。 /// -/// 加载时间低于此值时骨架屏只显示静态灰色块,避免 pulse 动画一闪而过。 -const SKELETON_PULSE_DELAY_MS: u32 = 200; +/// 加载时间低于此值时完全不显示骨架屏,避免闪烁。 +const SKELETON_DELAY_MS: u32 = 200; -/// 延迟 pulse 动画的骨架屏包装组件。 +/// 延迟渲染的骨架屏包装组件。 /// -/// 骨架屏区域**立即显示**(灰色静态占位块),避免空白闪烁。 -/// 延迟 `SKELETON_PULSE_DELAY_MS` 毫秒后,如果仍在加载,则启动 pulse 动画。 +/// 前 `SKELETON_DELAY_MS` 毫秒内不渲染任何内容; +/// 超过延迟后渲染子元素并启动 pulse 动画。 /// -/// 快加载(< 200ms):用户只看到静态灰色块一闪而过,无动画感知。 -/// 慢加载:灰色块正常 pulse,提示正在加载。 +/// 快加载(< 200ms):组件在渲染前就被卸载,用户完全看不到骨架屏。 +/// 慢加载:骨架屏正常显示并 pulse,提示正在加载。 #[component] pub fn DelayedSkeleton(children: Element) -> Element { - let mut pulsing = use_signal(|| false); + let mut visible = use_signal(|| false); - // 延迟启动 pulse 动画 use_effect(move || { spawn(async move { - sleep_ms(SKELETON_PULSE_DELAY_MS).await; - pulsing.set(true); + sleep_ms(SKELETON_DELAY_MS).await; + visible.set(true); }); }); - rsx! { - div { class: if pulsing() { "animate-pulse" } else { "" }, {children} } + if visible() { + rsx! { + div { class: "animate-pulse", {children} } + } + } else { + rsx! {} } } diff --git a/src/hooks/mod.rs b/src/hooks/mod.rs index b8c6d9a..b5ae98a 100644 --- a/src/hooks/mod.rs +++ b/src/hooks/mod.rs @@ -1,15 +1,13 @@ //! 共享的 Dioxus Hooks 模块。 //! //! 该模块集中管理可在组件树中复用的自定义 Hook,包括: -//! - 骨架屏延迟加载状态 //! - 通用 DOM 事件监听(注册 + 自动卸载清理) //! - 客户端分页数据加载 //! //! 评论草稿持久化(localStorage 读写)已迁至 `crate::utils::comment_storage`, //! 因为它是纯工具函数而非渲染期 Hook。 - -/// 延迟加载状态 Hook。 -pub mod delayed_loading; +//! +//! 骨架屏延迟渲染已统一至 `crate::components::skeletons::delayed_skeleton::DelayedSkeleton` 组件。 /// 通用 DOM 事件监听 Hook(add/remove_event_listener 生命周期封装)。 pub mod event_listener; diff --git a/src/pages/post_detail.rs b/src/pages/post_detail.rs index 29c1678..9a5052f 100644 --- a/src/pages/post_detail.rs +++ b/src/pages/post_detail.rs @@ -63,7 +63,9 @@ pub fn PostDetail(slug: String) -> Element { div { class: "mt-12 border-t border-gray-200 dark:border-gray-700 pt-8", SuspenseBoundary { fallback: move |_| rsx! { - crate::components::skeletons::comment_skeleton::CommentListSkeleton {} + DelayedSkeleton { + crate::components::skeletons::comment_skeleton::CommentListSkeleton {} + } }, crate::components::comments::section::CommentSection { post_id: post.id } }