feat: add photoswipe for image zoom (#135)

This commit is contained in:
dabuside
2024-07-28 01:41:11 +08:00
committed by saicaca
parent 336290a92f
commit 51025f0ea7
8 changed files with 173 additions and 95 deletions

8
src/global.d.ts vendored Normal file
View File

@@ -0,0 +1,8 @@
import type { AstroIntegration } from '@swup/astro'
declare global {
interface Window {
// type from '@swup/astro' is incorrect
swup: AstroIntegration
}
}

View File

@@ -1,53 +1,58 @@
---
import GlobalStyles from "@components/GlobalStyles.astro";
import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';
import ImageWrapper from "@components/misc/ImageWrapper.astro";
import GlobalStyles from '@components/GlobalStyles.astro'
import '@fontsource/roboto/400.css'
import '@fontsource/roboto/500.css'
import '@fontsource/roboto/700.css'
import ImageWrapper from '@components/misc/ImageWrapper.astro'
import ConfigCarrier from "@components/ConfigCarrier.astro";
import {profileConfig, siteConfig} from "@/config";
import {type Favicon} from "../types/config";
import {defaultFavicons} from "../constants/icon";
import {LIGHT_MODE, DARK_MODE, AUTO_MODE, DEFAULT_THEME} from "../constants/constants";
import {pathsEqual, url} from "../utils/url-utils";
import { profileConfig, siteConfig } from '@/config'
import ConfigCarrier from '@components/ConfigCarrier.astro'
import {
AUTO_MODE,
DARK_MODE,
DEFAULT_THEME,
LIGHT_MODE,
} from '../constants/constants'
import { defaultFavicons } from '../constants/icon'
import type { Favicon } from '../types/config'
import { url, pathsEqual } from '../utils/url-utils'
interface Props {
title?: string;
banner?: string;
description?: string;
title?: string
banner?: string
description?: string
}
let { title, banner, description } = Astro.props;
let { title, banner, description } = Astro.props
// apply a class to the body element to decide the height of the banner, only used for initial page load
// Swup can update the body for each page visit, but it's after the page transition, causing a delay for banner height change
// so use Swup hooks instead to change the height immediately when a link is clicked
const isHomePage = pathsEqual(Astro.url.pathname, url('/'));
const isHomePage = pathsEqual(Astro.url.pathname, url('/'))
// defines global css variables
// why doing this in Layout instead of GlobalStyles: https://github.com/withastro/astro/issues/6728#issuecomment-1502203757
const configHue = siteConfig.themeColor.hue;
const configHue = siteConfig.themeColor.hue
if (!banner || typeof banner !== 'string' || banner.trim() === '') {
banner = siteConfig.banner.src;
banner = siteConfig.banner.src
}
// TODO don't use post cover as banner for now
banner = siteConfig.banner.src;
banner = siteConfig.banner.src
const enableBanner = siteConfig.banner.enable;
const enableBanner = siteConfig.banner.enable
let pageTitle;
let pageTitle: string
if (title) {
pageTitle = `${title} - ${siteConfig.title}`;
pageTitle = `${title} - ${siteConfig.title}`
} else {
pageTitle = `${siteConfig.title} - ${siteConfig.subtitle}`;
pageTitle = `${siteConfig.title} - ${siteConfig.subtitle}`
}
const favicons: Favicon[] = siteConfig.favicon.length > 0 ? siteConfig.favicon : defaultFavicons
const favicons: Favicon[] =
siteConfig.favicon.length > 0 ? siteConfig.favicon : defaultFavicons
const siteLang = siteConfig.lang.replace('_', '-')
---
<!DOCTYPE html>

View File

@@ -1,23 +1,21 @@
---
import Layout from "./Layout.astro";
import Navbar from "@components/Navbar.astro";
import SideBar from "@components/widget/SideBar.astro";
import Footer from "@components/Footer.astro";
import BackToTop from "@components/control/BackToTop.astro";
import Footer from '@components/Footer.astro'
import Navbar from '@components/Navbar.astro'
import BackToTop from '@components/control/BackToTop.astro'
import SideBar from '@components/widget/SideBar.astro'
import Layout from './Layout.astro'
import { Icon } from 'astro-icon/components';
import { siteConfig } from "../config";
import { siteConfig } from '../config';
interface Props {
title?: string;
banner?: string;
description?: string;
title?: string
banner?: string
description?: string
}
const { title, banner, description } = Astro.props
const hasBannerCredit = siteConfig.banner.enable && siteConfig.banner.credit.enable
const hasBannerLink = !!(siteConfig.banner.credit.url)
---
<Layout title={title} banner={banner} description={description}>
@@ -46,7 +44,8 @@ const hasBannerLink = !!(siteConfig.banner.credit.url)
<div id="content-wrapper" class="row-start-2 row-end-3 col-span-2 lg:col-span-1 overflow-hidden onload-animation">
<!-- the overflow-hidden here prevent long text break the layout-->
<main id="swup" class="transition-swup-fade">
<!-- make id different from windows.swup global property -->
<main id="swup-container" class="transition-swup-fade">
<slot></slot>
</main>

View File

@@ -1,21 +1,20 @@
---
import MainGridLayout from "../layouts/MainGridLayout.astro";
import Pagination from "../components/control/Pagination.astro";
import {getSortedPosts} from "../utils/content-utils";
import {PAGE_SIZE} from "../constants/constants";
import PostPage from "../components/PostPage.astro";
import {type GetStaticPaths} from "astro";
import type { GetStaticPaths } from 'astro'
import PostPage from '../components/PostPage.astro'
import Pagination from '../components/control/Pagination.astro'
import { PAGE_SIZE } from '../constants/constants'
import MainGridLayout from '../layouts/MainGridLayout.astro'
import { getSortedPosts } from '../utils/content-utils'
export const getStaticPaths = (async ({ paginate }) => {
const allBlogPosts = await getSortedPosts();
return paginate(allBlogPosts, { pageSize: PAGE_SIZE });
const allBlogPosts = await getSortedPosts()
return paginate(allBlogPosts, { pageSize: PAGE_SIZE })
}) satisfies GetStaticPaths
// https://github.com/withastro/astro/issues/6507#issuecomment-1489916992
const {page} = Astro.props;
const len = page.data.length;
const { page } = Astro.props
const len = page.data.length
---
<MainGridLayout>

View File

@@ -1,48 +1,48 @@
---
import { getCollection } from 'astro:content';
import MainGridLayout from "@layouts/MainGridLayout.astro";
import ImageWrapper from "../../components/misc/ImageWrapper.astro";
import {Icon} from "astro-icon/components";
import PostMetadata from "../../components/PostMeta.astro";
import {i18n} from "@i18n/translation";
import I18nKey from "@i18n/i18nKey";
import {getDir, getPostUrlBySlug} from "@utils/url-utils";
import License from "@components/misc/License.astro";
import {licenseConfig} from "src/config";
import Markdown from "@components/misc/Markdown.astro";
import path from "path";
import {profileConfig} from "../../config";
import {formatDateToYYYYMMDD} from "../../utils/date-utils";
import path from 'node:path'
import { getCollection } from 'astro:content'
import License from '@components/misc/License.astro'
import Markdown from '@components/misc/Markdown.astro'
import I18nKey from '@i18n/i18nKey'
import { i18n } from '@i18n/translation'
import MainGridLayout from '@layouts/MainGridLayout.astro'
import { getDir, getPostUrlBySlug } from '@utils/url-utils'
import { Icon } from 'astro-icon/components'
import { licenseConfig } from 'src/config'
import PostMetadata from '../../components/PostMeta.astro'
import ImageWrapper from '../../components/misc/ImageWrapper.astro'
import { profileConfig } from '../../config'
import { formatDateToYYYYMMDD } from '../../utils/date-utils'
export async function getStaticPaths() {
const blogEntries = await getCollection('posts', ({ data }) => {
return import.meta.env.PROD ? data.draft !== true : true;
});
return blogEntries.map(entry => ({
params: { slug: entry.slug }, props: { entry },
}));
const blogEntries = await getCollection('posts', ({ data }) => {
return import.meta.env.PROD ? data.draft !== true : true
})
return blogEntries.map(entry => ({
params: { slug: entry.slug },
props: { entry },
}))
}
const { entry } = Astro.props;
const { Content } = await entry.render();
const { entry } = Astro.props
const { Content } = await entry.render()
const { remarkPluginFrontmatter } = await entry.render();
const { remarkPluginFrontmatter } = await entry.render()
const jsonLd = {
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": entry.data.title,
"description": entry.data.description || entry.data.title,
"keywords": entry.data.tags,
"author": {
"@type": "Person",
"name": profileConfig.name,
"url": Astro.site
},
"datePublished": formatDateToYYYYMMDD(entry.data.published),
// TODO include cover image here
'@context': 'https://schema.org',
'@type': 'BlogPosting',
headline: entry.data.title,
description: entry.data.description || entry.data.title,
keywords: entry.data.tags,
author: {
'@type': 'Person',
name: profileConfig.name,
url: Astro.site,
},
datePublished: formatDateToYYYYMMDD(entry.data.published),
// TODO include cover image here
}
---
<MainGridLayout banner={entry.data.image} title={entry.data.title} description={entry.data.description}>
<script is:inline slot="head" type="application/ld+json" set:html={JSON.stringify(jsonLd)}></script>
@@ -137,5 +137,61 @@ const jsonLd = {
#post-container :nth-child(3) { animation-delay: calc(var(--content-delay) + 100ms) }
#post-container :nth-child(4) { animation-delay: calc(var(--content-delay) + 175ms) }
#post-container :nth-child(5) { animation-delay: calc(var(--content-delay) + 250ms) }
#post-container :nth-child(6) { animation-delay: calc(var(--content-delay) + 325ms) }
</style>
#post-container :nth-child(6) { animation-delay: calc(var(--content-delay) + 325ms) }
</style>
<script>
import PhotoSwipeLightbox from "photoswipe/lightbox"
import "photoswipe/style.css"
let lightbox: PhotoSwipeLightbox
function createPhotoSwipe() {
lightbox = new PhotoSwipeLightbox({
gallery: "#post-container img",
pswpModule: () => import("photoswipe"),
})
lightbox.addFilter("domItemData", (itemData, element, linkEl) => {
if (element instanceof HTMLImageElement) {
itemData.src = element.src
itemData.w = Number(element.getAttribute("width"))
itemData.h = Number(element.getAttribute("height"))
itemData.msrc = element.src
}
return itemData
})
lightbox.init()
}
const setup = () => {
if (!lightbox) {
createPhotoSwipe()
}
window.swup.hooks.on("page:view", () => {
createPhotoSwipe()
})
window.swup.hooks.on(
"content:replace",
() => {
console.log("content:replace")
lightbox?.destroy?.()
},
{ before: true },
)
}
if (window.swup) {
setup()
} else {
document.addEventListener("swup:enable", setup)
}
</script>