--- import { Image, getImage } from 'astro:assets'; import { getCollection } from 'astro:content'; import BaseLayout from '../../layouts/BaseLayout.astro'; import { t } from '../../i18n/ui'; import type { Locale } from '../../lib/locale'; import type { PhotoExif } from '../../lib/exif'; // /en/ 侧由 T4 薄页传 locale='en';zh 路由无人传参,默认 zh 零回归 interface Props { locale?: Locale; } const { locale = 'zh' } = Astro.props; const photos = await getCollection('photos'); // EXIF 参数条(机身/镜头/光圈/快门/ISO)构建期拼好整串,灯箱运行时只做文本替换 function formatExif(exif: PhotoExif | undefined): string { if (!exif) return ''; const shutter = exif.exposureTime === undefined ? undefined : exif.exposureTime >= 1 ? `${exif.exposureTime}s` : `1/${Math.round(1 / exif.exposureTime)}s`; return [ [exif.make, exif.model].filter(Boolean).join(' ') || undefined, exif.lens, exif.fNumber === undefined ? undefined : `f/${exif.fNumber}`, shutter, exif.iso === undefined ? undefined : `ISO ${exif.iso}`, ] .filter(Boolean) .join(' · '); } /* masonry:行高/列距用容器宽百分比(0.92cqw/1.62cqw,在 864px 版心即 8px/14px 档), 行高随断点等比缩放,构建期一份 row-span 在 2/3 列全断点精确成立—— px 定值行高会让格高随列宽漂移,窄屏裁切失控 */ const ROW = 0.0092; const GAP = 0.0162; const spanFor = (w: number, h: number, cols: number) => Math.round(((1 - GAP * (cols - 1)) / (cols * (w / h)) + GAP) / (ROW + GAP)); const cards = await Promise.all( photos.map(async (photo) => { // 宽高比来源:photos loader 产出 ImageMetadata 的 width/height(orientation 已修正) const w = photo.data.image.width ?? 4; const h = photo.data.image.height ?? 3; const full = await getImage({ src: photo.data.image, width: 1440 }); return { group: photo.data.group, title: photo.data.title, alt: photo.data.alt ?? photo.data.title, image: photo.data.image, exif: formatExif(photo.data.exif), fullSrc: full.src, // 原图走仓外 store(构建时独立 rsync 到 /photos-originals/),git 不背 196M original: `/photos-originals/${photo.id}.jpg`, w, h, s3: spanFor(w, h, 3), s2: spanFor(w, h, 2), }; }), ); const first = cards[0]; --- {t('gallery_title', locale)} {t('gallery_filter_legend', locale)} {t('gallery_filter_all', locale)} {t('gallery_filter_street', locale)} {t('gallery_filter_landscape', locale)} {cards.map((card, i) => ( ))} {t('gallery_count', locale).replace('{n}', String(cards.length))} {first?.title} {first?.exif} 1 / {cards.length} {t('gallery_lightbox_original', locale)} {t('gallery_lightbox_download', locale)}
{t('gallery_count', locale).replace('{n}', String(cards.length))}