From 65dc754cae47e8ce762ce34492edb3f960153f5f Mon Sep 17 00:00:00 2001 From: Ad-closeNN <1709301095@qq.com> Date: Sun, 19 Apr 2026 22:58:21 +0800 Subject: [PATCH] =?UTF-8?q?feat(ui):=20PostCard=20=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E9=A1=B5=E6=96=B0=E5=A2=9E=E6=B5=8F=E8=A7=88=E9=87=8F=E6=98=BE?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- src/global.d.ts | 26 +++++++- src/layouts/Layout.astro | 105 ++++++++++++++++++++++++-------- src/pages/posts/[...slug].astro | 46 +++++++++++++- 4 files changed, 152 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index 4463309..72c5b32 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,6 @@ yarn.lock src/content/.obsidian -.playwright-mcp \ No newline at end of file +.playwright-mcp +.serena +.claude \ No newline at end of file diff --git a/src/global.d.ts b/src/global.d.ts index 8d4042b..f6c8201 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -1,9 +1,26 @@ -import type { AstroIntegration } from "@swup/astro"; +export {}; + +type SwupHookHandler = (...args: unknown[]) => void; + +type SwupLike = { + hooks: { + on: (eventName: string, handler: SwupHookHandler) => void; + }; +}; + +type BlogPhotoSwipeState = { + lightbox: { + destroy: () => void; + } | null; + hookRegistered: boolean; + pageLoadRegistered: boolean; +}; declare global { interface Window { - // type from '@swup/astro' is incorrect - swup: AstroIntegration; + swup?: SwupLike; + __blogPhotoSwipe?: BlogPhotoSwipeState; + dataLayer?: unknown[]; pagefind: { search: (query: string) => Promise<{ results: Array<{ @@ -14,6 +31,9 @@ declare global { } } +declare function gtag(...args: unknown[]): void; +declare let dataLayer: unknown[]; + interface SearchResult { url: string; meta: { diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 4a902a2..50f5b67 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -482,8 +482,8 @@ function showBanner() { } // 在显示Banner前,先移除所有已加载的onload-animation类,防止动画重复触发 - const animatedElements = document.querySelectorAll('.onload-animation'); - animatedElements.forEach(el => { + const animatedElements = document.querySelectorAll('.onload-animation'); + animatedElements.forEach((el) => { el.style.animation = 'none'; el.style.opacity = '1'; }); @@ -635,52 +635,109 @@ window.onresize = () => { import PhotoSwipeLightbox from "photoswipe/lightbox" import "photoswipe/style.css" -let lightbox: PhotoSwipeLightbox -let pswp = import("photoswipe") +const zoomTargetSelector = ".custom-md img, #post-cover img" +const pswpModule = import("photoswipe") + +type PhotoSwipeState = { + lightbox: PhotoSwipeLightbox | null + hookRegistered: boolean +} + +const photoSwipeState = (window.__blogPhotoSwipe ??= { + lightbox: null, + hookRegistered: false, + pageLoadRegistered: false, +}) as PhotoSwipeState & { pageLoadRegistered: boolean } + +function getZoomTargets() { + return Array.from(document.querySelectorAll(zoomTargetSelector)) +} + +function destroyPhotoSwipe() { + photoSwipeState.lightbox?.destroy() + photoSwipeState.lightbox = null +} function createPhotoSwipe() { - lightbox = new PhotoSwipeLightbox({ - gallery: ".custom-md img, #post-cover img", - pswpModule: () => pswp, + const lightbox = new PhotoSwipeLightbox({ + gallery: "body", + children: zoomTargetSelector, + pswpModule: () => pswpModule, closeSVG: '', zoomSVG: '', padding: { top: 20, bottom: 20, left: 20, right: 20 }, + showHideAnimationType: "fade", + showAnimationDuration: 160, + hideAnimationDuration: 140, wheelToZoom: true, arrowPrev: false, arrowNext: false, - imageClickAction: 'close', - tapAction: 'close', - doubleTapAction: 'zoom', + imageClickAction: "close", + tapAction: "close", + doubleTapAction: "zoom", }) lightbox.addFilter("domItemData", (itemData, element) => { if (element instanceof HTMLImageElement) { - itemData.src = element.src + const width = element.naturalWidth || element.width || window.innerWidth + const height = element.naturalHeight || element.height || window.innerHeight + const src = element.currentSrc || element.src - itemData.w = Number(element.naturalWidth || window.innerWidth) - itemData.h = Number(element.naturalHeight || window.innerHeight) - - itemData.msrc = element.src + itemData.src = src + itemData.w = Number(width) + itemData.h = Number(height) + itemData.msrc = src } return itemData }) lightbox.init() + photoSwipeState.lightbox = lightbox } -const setup = () => { - window.swup.hooks.on("page:view", () => { - if (lightbox) { - lightbox.destroy() - } - createPhotoSwipe() +function initPhotoSwipe() { + if (photoSwipeState.lightbox || getZoomTargets().length === 0) { + return + } + createPhotoSwipe() +} + +function reinitPhotoSwipe() { + destroyPhotoSwipe() + window.requestAnimationFrame(() => { + initPhotoSwipe() }) } -if (window.swup) { - setup() +function registerPhotoSwipeHook() { + if (photoSwipeState.hookRegistered || !window.swup?.hooks) { + return + } + window.swup.hooks.on("page:view", reinitPhotoSwipe) + photoSwipeState.hookRegistered = true +} + +function scheduleInitialPhotoSwipe() { + window.requestAnimationFrame(() => { + initPhotoSwipe() + }) +} + +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", scheduleInitialPhotoSwipe, { once: true }) } else { - document.addEventListener("swup:enable", setup) + scheduleInitialPhotoSwipe() +} + +if (!photoSwipeState.pageLoadRegistered) { + document.addEventListener("astro:page-load", scheduleInitialPhotoSwipe) + photoSwipeState.pageLoadRegistered = true +} + +if (window.swup?.hooks) { + registerPhotoSwipeHook() +} else { + document.addEventListener("swup:enable", registerPhotoSwipeHook, { once: true }) } diff --git a/src/pages/posts/[...slug].astro b/src/pages/posts/[...slug].astro index 7a2b251..11aa989 100644 --- a/src/pages/posts/[...slug].astro +++ b/src/pages/posts/[...slug].astro @@ -29,6 +29,7 @@ interface Props { const { entry }: Props = Astro.props; const { Content, headings, remarkPluginFrontmatter } = await render(entry); +const postSlug = entry.id; const jsonLd = { "@context": "https://schema.org", @@ -62,7 +63,7 @@ const customcover = entry.data.customcover; {} ]}> -
+
@@ -77,6 +78,12 @@ const customcover = entry.data.customcover; 大约 {remarkPluginFrontmatter.minutes} {" " + i18n(remarkPluginFrontmatter.minutes === 1 ? I18nKey.minuteCount : I18nKey.minutesCount)}
+
+
+ +
+
加载中...
+
@@ -164,6 +171,43 @@ const customcover = entry.data.customcover;
+ +