diff --git a/public/js/lightbox.js b/public/js/lightbox.js index 1908a3b..c941df0 100644 --- a/public/js/lightbox.js +++ b/public/js/lightbox.js @@ -92,7 +92,7 @@ // ============ 图像收集 ============ // 收集所有 selectors命中的 .blur-img 节点。 - // gallery: 正文图(组成图集);singles: 带 data-single 的单张图(如封面)。 + // gallery: 正文图(组成图集);singles: 带 lightbox-single class 的单张图(如封面)。 function collectImages(roots) { var gallery = []; var singles = []; @@ -100,7 +100,7 @@ var nodes = roots[i].querySelectorAll(".blur-img"); for (var j = 0; j < nodes.length; j++) { var n = nodes[j]; - if (n.getAttribute("data-single")) { + if (n.classList.contains("lightbox-single")) { singles.push(n); } else { gallery.push(n); @@ -124,7 +124,8 @@ var origSrc = originalUrl(dataSrc); var altText = fullImg.getAttribute("alt") || ""; var isSingle = - originNode.getAttribute("data-single") === "true" || gallery.length === 0; + originNode.classList.contains("lightbox-single") || + gallery.length === 0; var vw = window.innerWidth; var vh = window.innerHeight; diff --git a/src/components/post/post_content.rs b/src/components/post/post_content.rs index e4cddfe..75fe4e8 100644 --- a/src/components/post/post_content.rs +++ b/src/components/post/post_content.rs @@ -19,7 +19,7 @@ pub fn PostContent(content_html: String) -> Element { let _ = js_sys::eval(include_str!("../../../public/js/post-content.js")); let _ = js_sys::eval(include_str!("../../../public/js/lightbox.js")); let _ = js_sys::eval("window.__initPostContent('.post-content')"); - // 正文图组成图集;封面(.entry-cover)单张模式,由 PostCover 标记 data-single。 + // 正文图组成图集;封面(.entry-cover,带 lightbox-single class)单张模式。 let _ = js_sys::eval("window.__initLightbox(['.post-content', '.entry-cover'])"); }); diff --git a/src/components/post/post_cover.rs b/src/components/post/post_cover.rs index 988de85..168a9c3 100644 --- a/src/components/post/post_cover.rs +++ b/src/components/post/post_cover.rs @@ -1,26 +1,66 @@ //! 文章封面组件 //! -//! 在文章详情页渲染封面大图,使用图片查看器支持点击放大。 +//! 在文章详情页渲染封面大图,使用 blur-up 双层结构与 lightbox.js 灯箱。 +//! 封面为单张(data-single),不参与正文图集切换。 use dioxus::prelude::*; -use crate::components::image_viewer::ImageViewer; - /// 文章封面组件。 /// /// Props: /// - `src`:封面原图 URL /// -/// 使用 1200px 宽度的缩略图作为默认封面展示,点击可放大查看。 +/// 渲染 `.blur-img` 结构,与正文图片一致;`data-single="true"` 标记为单张, +/// 由 `lightbox.js` 接管点击放大(原地缩放飞出 + 原图展示)。 +/// 服务端读取真实尺寸写入 `--ar`,确保占位期间维持正确宽高比。 #[component] pub fn PostCover(src: String) -> Element { + // SSR 时读真实尺寸算 --ar;WASM 端不读(HTML 已在 SSR 写入)。 + let ar_style = { + #[cfg_attr(not(feature = "server"), allow(unused_mut))] + let mut s = String::new(); + #[cfg(feature = "server")] + { + if let Some(rel) = src + .strip_prefix("/uploads/") + .map(|p| p.split('?').next().unwrap_or(p)) + { + if let Some((w, h)) = crate::api::image::get_image_dimensions(rel) { + // CSS aspect-ratio 用斜杠分隔(width / height) + s = format!("--ar:{} / {};", w, h); + } + } + } + s + }; + + // 占位图 ?w=20,展示图 ?w=1200;灯箱原图由 lightbox.js 去 query 得到。 + let placeholder_src = if src.contains('?') { + format!("{}&w=20", src.split('?').next().unwrap_or(&src)) + } else { + format!("{}?w=20", src) + }; + let full_src = if src.contains('?') { + format!("{}&w=1200", src.split('?').next().unwrap_or(&src)) + } else { + format!("{}?w=1200", src) + }; + rsx! { figure { class: "entry-cover", - ImageViewer { - src: src.clone(), - thumb_params: "?w=1200", - alt: "封面图片".to_string(), - lazy_load: false, + span { + class: "blur-img entry-cover-blur lightbox-single", + style: "{ar_style}", + img { + class: "blur-img-placeholder", + src: "{placeholder_src}", + alt: "封面图片", + } + img { + class: "blur-img-full", + "data-src": "{full_src}", + alt: "封面图片", + } } } }