Skip to main content

yggdrasil/components/post/
post_cover.rs

1//! 文章封面组件
2//!
3//! 在文章详情页渲染封面大图,使用 blur-up 双层结构与 lightbox.js 灯箱。
4//! 封面为单张(data-single),不参与正文图集切换。
5
6use leptos::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(
19    #[prop(into)] src: String,
20    #[prop(optional, into)] post_id: MaybeProp<i32>,
21) -> impl IntoView {
22    // SSR 时读真实尺寸算 --ar;WASM 端不读(HTML 已在 SSR 写入)。
23    let ar_style = {
24        #[cfg_attr(not(feature = "server"), allow(unused_mut))]
25        let mut s = String::new();
26        #[cfg(feature = "server")]
27        {
28            if let Some(rel) = src
29                .strip_prefix("/uploads/")
30                .map(|p| p.split('?').next().unwrap_or(p))
31            {
32                if let Some((w, h)) = crate::api::image::get_image_dimensions(rel) {
33                    // CSS aspect-ratio 用斜杠分隔(width / height)
34                    s = format!("--ar:{} / {};", w, h);
35                }
36            }
37        }
38        s
39    };
40
41    // 占位图 ?w=20,展示图 ?w=1200;灯箱原图由 lightbox.js 去 query 得到。
42    let placeholder_src = if src.contains('?') {
43        format!("{}&w=20", src.split('?').next().unwrap_or(&src))
44    } else {
45        format!("{}?w=20", src)
46    };
47    let full_src = if src.contains('?') {
48        format!("{}&w=1200", src.split('?').next().unwrap_or(&src))
49    } else {
50        format!("{}?w=1200", src)
51    };
52    let post_id = post_id.get_untracked();
53
54    view! {
55        <figure
56            class="entry-cover"
57            data-vt-post-id=post_id.map(|id| id.to_string())
58            data-vt-role=post_id.map(|_| "cover")
59        >
60            <span class="blur-img entry-cover-blur lightbox-single" style=ar_style>
61                <img class="blur-img-placeholder" src=placeholder_src alt="封面图片"/>
62                <img class="blur-img-full" data-src=full_src alt="封面图片"/>
63            </span>
64        </figure>
65    }
66}