refactor(skeletons): 统一骨架屏延迟机制,200ms 内加载不显示
- DelayedSkeleton 从「延迟 pulse 动画」改为「延迟渲染」: 前 200ms 完全不渲染,超时后才显示并带 pulse 动画 - admin_layout 从 use_delayed_loading hook 改为 DelayedSkeleton 包裹 - CommentListSkeleton 的两处裸用(section.rs / post_detail.rs) 补上 DelayedSkeleton 包裹 - 移除不再使用的 use_delayed_loading hook 模块
This commit is contained in:
parent
5b9e65bf72
commit
61c64841f9
@ -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::<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 {} },
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {} }
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -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! {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 }
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user