Skip to main content

yggdrasil/components/post/
post_cover.rs

1//! 文章封面组件
2//!
3//! 在文章详情页渲染封面大图,使用 blur-up 双层结构与 lightbox.js 灯箱。
4//! 封面为单张(data-single),不参与正文图集切换。
5
6use dioxus::prelude::*;
7
8/// 文章封面组件。
9///
10/// Props:
11/// - `src`:封面原图 URL
12/// - `post_id`:参与文章共享过渡的文章 ID(可选)
13///
14/// 渲染 `.blur-img` 结构,与正文图片一致;`data-single="true"` 标记为单张,
15/// 由 `lightbox.js` 接管点击放大(原地缩放飞出 + 原图展示)。
16/// 服务端读取真实尺寸写入 `--ar`,确保占位期间维持正确宽高比。
17#[component]
18pub fn PostCover(src: String, #[props(default)] post_id: Option<i32>) -> Element {
19    // SSR 时读真实尺寸算 --ar;WASM 端不读(HTML 已在 SSR 写入)。
20    let ar_style = {
21        #[cfg_attr(not(feature = "server"), allow(unused_mut))]
22        let mut s = String::new();
23        #[cfg(feature = "server")]
24        {
25            if let Some(rel) = src
26                .strip_prefix("/uploads/")
27                .map(|p| p.split('?').next().unwrap_or(p))
28            {
29                if let Some((w, h)) = crate::api::image::get_image_dimensions(rel) {
30                    // CSS aspect-ratio 用斜杠分隔(width / height)
31                    s = format!("--ar:{} / {};", w, h);
32                }
33            }
34        }
35        s
36    };
37
38    // 占位图 ?w=20,展示图 ?w=1200;灯箱原图由 lightbox.js 去 query 得到。
39    let placeholder_src = if src.contains('?') {
40        format!("{}&w=20", src.split('?').next().unwrap_or(&src))
41    } else {
42        format!("{}?w=20", src)
43    };
44    let full_src = if src.contains('?') {
45        format!("{}&w=1200", src.split('?').next().unwrap_or(&src))
46    } else {
47        format!("{}?w=1200", src)
48    };
49
50    rsx! {
51        figure { class: "entry-cover",
52            "data-vt-post-id": post_id.map(|id| id.to_string()),
53            "data-vt-role": post_id.map(|_| "cover"),
54            span {
55                class: "blur-img entry-cover-blur lightbox-single",
56                style: "{ar_style}",
57                img {
58                    class: "blur-img-placeholder",
59                    src: "{placeholder_src}",
60                    alt: "封面图片",
61                }
62                img {
63                    class: "blur-img-full",
64                    "data-src": "{full_src}",
65                    alt: "封面图片",
66                }
67            }
68        }
69    }
70}