Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions src/components/common/CoverImage.astro
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
import { Picture } from "astro:assets";
import { getImage, Picture } from "astro:assets";
import * as path from "node:path";
import type { ImageMetadata } from "astro";
import { coverImageConfig } from "@/config";
import { coverImageConfig, siteConfig } from "@/config";
import type { ImageFormat, ResponsiveImageLayout } from "@/types/config";
import {
getFallbackFormat,
Expand Down Expand Up @@ -136,6 +136,26 @@ const remoteReferrerPolicy =
!isLocal && shouldAddNoReferrer(isPublic ? url(src) : src)
? "no-referrer"
: undefined;

// Generate tiny image for local or public images if available
let tinyImgUrl = "";
if (siteConfig.post.generateTinyThumbnails) {
if (isLocal && img) {
try {
const tinyImg = await getImage({ src: img, width: 32, quality: 30 });
tinyImgUrl = tinyImg.src;
} catch (e) {
console.error("Failed to generate tiny thumbnail:", e);
}
} else if (isPublic && src) {
try {
const tinyImg = await getImage({ src: src, width: 32, quality: 30 });
tinyImgUrl = tinyImg.src;
} catch (e) {
console.error("Failed to generate tiny thumbnail for public image:", e);
}
}
}
---

<div
Expand All @@ -145,7 +165,12 @@ const remoteReferrerPolicy =
data-api-urls={apiUrls.length > 0 ? JSON.stringify(apiUrls) : undefined}
>
<!-- LQIP 渐变占位 -->
<div class="lqip-placeholder absolute inset-0 pointer-events-none" style={lqipProps.style} aria-hidden="true"></div>
<div
class="lqip-placeholder absolute inset-0 pointer-events-none"
style={`${lqipProps.style};${tinyImgUrl ? `--tiny-src: url(${tinyImgUrl})` : ''}`}
data-tiny-src={tinyImgUrl ? "true" : undefined}
aria-hidden="true"
></div>

<!-- 加载动画 -->
{showLoading && (
Expand Down
30 changes: 28 additions & 2 deletions src/components/common/ImageWrapper.astro
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
import { Image, Picture } from "astro:assets";
import { getImage, Image, Picture } from "astro:assets";
import * as path from "node:path";
import type { ImageMetadata } from "astro";
import type { ImageFormat, ResponsiveImageLayout } from "@/types/config";
Expand All @@ -11,6 +11,7 @@ import {
} from "@/utils/image-utils";
import { getLqipProps } from "@/utils/lqip-utils";
import { url } from "@/utils/url-utils";
import { siteConfig } from "@/config";

interface Props {
id?: string;
Expand Down Expand Up @@ -103,9 +104,34 @@ const lqipProps = isLocal
: isPublic
? getLqipProps(src, basePath, true)
: getLqipProps(src);

// Generate tiny image for local or public images if available
let tinyImgUrl = "";
if (siteConfig.post.generateTinyThumbnails) {
if (isLocal && img) {
try {
const tinyImg = await getImage({ src: img, width: 32, quality: 30 });
tinyImgUrl = tinyImg.src;
} catch (e) {
console.error("Failed to generate tiny thumbnail:", e);
}
} else if (isPublic && src) {
try {
const tinyImg = await getImage({ src: src, width: 32, quality: 30 });
tinyImgUrl = tinyImg.src;
} catch (e) {
console.error("Failed to generate tiny thumbnail for public image:", e);
}
}
}
---
<div id={id} class:list={[className, 'overflow-hidden relative']}>
<div class="lqip-placeholder absolute inset-0 pointer-events-none" style={lqipProps.style} aria-hidden="true"></div>
<div
class="lqip-placeholder absolute inset-0 pointer-events-none"
style={`${lqipProps.style};${tinyImgUrl ? `--tiny-src: url(${tinyImgUrl})` : ''}`}
data-tiny-src={tinyImgUrl ? "true" : undefined}
aria-hidden="true"
></div>
<div class="transition absolute inset-0 dark:bg-black/10 pointer-events-none"></div>
{isLocal && img && usePicture && (
<Picture
Expand Down
4 changes: 4 additions & 0 deletions src/components/controls/DisplaySettingsIntegrated.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ function toggleSakuraEnabled() {
setSakuraEnabled(sakuraEnabled);
}



function switchWallpaperMode(newMode: WALLPAPER_MODE) {
wallpaperMode = newMode;
setWallpaperMode(newMode);
Expand Down Expand Up @@ -746,6 +748,8 @@ $effect(() => {
</div>
{/if}



<!-- Layout Switch Section -->
{#if allowLayoutSwitch}
<div class="mt-2 mb-2">
Expand Down
19 changes: 18 additions & 1 deletion src/components/pages/gallery/AlbumCard.astro
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
---
import { getImage } from "astro:assets";
import I18nKey from "@/i18n/i18nKey";
import { i18n } from "@/i18n/translation";
import { getLqipProps } from "@/utils/lqip-utils";
import { url } from "@/utils/url-utils";
import { siteConfig } from "@/config";

interface Props {
id: string;
Expand Down Expand Up @@ -30,6 +32,16 @@ const {

// LQIP 渐变占位
const lqipProps = cover ? getLqipProps(cover, undefined, true) : { style: "" };

let tinyImgUrl = "";
if (siteConfig.post.generateTinyThumbnails && cover) {
try {
const tinyImg = await getImage({ src: cover, width: 32, quality: 30 });
tinyImgUrl = tinyImg.src;
} catch (e) {
console.error("Failed to generate tiny thumbnail:", e);
}
}
---

<a
Expand All @@ -41,7 +53,12 @@ const lqipProps = cover ? getLqipProps(cover, undefined, true) : { style: "" };
<div class="aspect-4/3 relative overflow-hidden">
{cover ? (
<>
<div class="lqip-placeholder absolute inset-0 pointer-events-none" style={lqipProps.style} aria-hidden="true"></div>
<div
class="lqip-placeholder absolute inset-0 pointer-events-none"
style={`${lqipProps.style};${tinyImgUrl ? `--tiny-src: url(${tinyImgUrl})` : ''}`}
data-tiny-src={tinyImgUrl ? "true" : undefined}
aria-hidden="true"
></div>
<img
src={cover}
alt={name}
Expand Down
19 changes: 18 additions & 1 deletion src/components/pages/gallery/PhotoCard.astro
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
---
import { getImage } from "astro:assets";
import { getLqipProps } from "@/utils/lqip-utils";
import { siteConfig } from "@/config";

interface Props {
src: string;
Expand All @@ -11,6 +13,16 @@ const { src, albumId, alt = "" } = Astro.props;

// LQIP 渐变占位
const lqipProps = getLqipProps(src, undefined, true);

let tinyImgUrl = "";
if (siteConfig.post.generateTinyThumbnails) {
try {
const tinyImg = await getImage({ src, width: 32, quality: 30 });
tinyImgUrl = tinyImg.src;
} catch (e) {
console.error("Failed to generate tiny thumbnail:", e);
}
}
---

<div class="gallery-photo-card break-inside-avoid mb-3">
Expand All @@ -20,7 +32,12 @@ const lqipProps = getLqipProps(src, undefined, true);
data-type="image"
class="block rounded-xl overflow-hidden relative group cursor-pointer"
>
<div class="lqip-placeholder absolute inset-0 pointer-events-none" style={lqipProps.style} aria-hidden="true"></div>
<div
class="lqip-placeholder absolute inset-0 pointer-events-none"
style={`${lqipProps.style};${tinyImgUrl ? `--tiny-src: url(${tinyImgUrl})` : ''}`}
data-tiny-src={tinyImgUrl ? "true" : undefined}
aria-hidden="true"
></div>
<img
src={src}
alt={alt}
Expand Down
2 changes: 2 additions & 0 deletions src/config/siteConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ export const siteConfig: SiteConfig = {
sharePoster: true,
// OpenGraph图片功能,注意开启后要渲染很长时间,不建议本地调试的时候开启
generateOgImages: false,
// 是否为图片生成渐进式加载所需的 32px 微缩图(构建时),开启后会增加构建时间,默认关闭
generateTinyThumbnails: false,
},

// bangumi配置
Expand Down
32 changes: 16 additions & 16 deletions src/constants/lqips.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,6 @@
"src:assets/images/avatar.avif": "dcd8d5d1cecabea9a9",
"public:gallery/firefly-2026/cover.avif": "7588a28a8f989e8d88",
"public:gallery/firefly-2026/1.avif": "c8bca9dde6d2babfb5",
"src:assets/images/DesktopWallpaper/d6.avif": "d9e5ec9ca7aebda9b0",
"src:assets/images/DesktopWallpaper/d5.avif": "e2dbe0e1dae1d9c9cd",
"src:assets/images/DesktopWallpaper/d4.avif": "727991b7a7bbe2b8c2",
"src:assets/images/DesktopWallpaper/d3.avif": "4268936f829e99adc7",
"src:assets/images/DesktopWallpaper/d2.avif": "b49db67a7e91d0aabc",
"src:assets/images/DesktopWallpaper/d1.avif": "8b9bb59ba6bae7d7dd",
"src:assets/images/MobileWallpaper/m6.avif": "a08eaac3abbfc7bdc8",
"src:assets/images/MobileWallpaper/m5.avif": "af99aecab1c1d2c2cb",
"src:assets/images/MobileWallpaper/m4.avif": "f0e1e2d4bfc7c2abc1",
"src:assets/images/MobileWallpaper/m3.avif": "c3a6b4dac2cad2c0c9",
"src:assets/images/MobileWallpaper/m2.avif": "f1e0ded8c2c6bbb0bc",
"src:assets/images/MobileWallpaper/m1.avif": "cccce0cbc0d59294c0",
"public:assets/images/sponsor/wechat.png": "d5d5d5dad9d7dbd8cf",
"public:assets/images/sponsor/alipay.png": "8ebbde91bedf93c0e0",
"public:assets/images/ad/ad1.webp": "a1b8b68a8e918c8f93",
"src:content/posts/images/vitepress.avif": "3d3d43e1e1e7ebe0da",
"src:content/posts/images/right-grid2.avif": "cbcdcddddcd9d8d7db",
"src:content/posts/images/obsidian.avif": "3a4145dce3e6e8ded8",
Expand All @@ -33,5 +18,20 @@
"src:content/posts/images/both-list.avif": "e5e7e4e6e8e7e3e3e5",
"src:content/posts/images/both-grid.avif": "d5d6d3e6e7e3d8d9e3",
"src:content/posts/images/1.avif": "b5b0b3bcb6bbe0e0df",
"src:content/posts/guide/cover.avif": "c8bca9dde6d2babfb5"
"src:content/posts/guide/cover.avif": "c8bca9dde6d2babfb5",
"src:assets/images/MobileWallpaper/m6.avif": "a08eaac3abbfc7bdc8",
"src:assets/images/MobileWallpaper/m5.avif": "af99aecab1c1d2c2cb",
"src:assets/images/MobileWallpaper/m4.avif": "f0e1e2d4bfc7c2abc1",
"src:assets/images/MobileWallpaper/m3.avif": "c3a6b4dac2cad2c0c9",
"src:assets/images/MobileWallpaper/m2.avif": "f1e0ded8c2c6bbb0bc",
"src:assets/images/MobileWallpaper/m1.avif": "cccce0cbc0d59294c0",
"src:assets/images/DesktopWallpaper/d6.avif": "d9e5ec9ca7aebda9b0",
"src:assets/images/DesktopWallpaper/d5.avif": "e2dbe0e1dae1d9c9cd",
"src:assets/images/DesktopWallpaper/d4.avif": "727991b7a7bbe2b8c2",
"src:assets/images/DesktopWallpaper/d3.avif": "4268936f829e99adc7",
"src:assets/images/DesktopWallpaper/d2.avif": "b49db67a7e91d0aabc",
"src:assets/images/DesktopWallpaper/d1.avif": "8b9bb59ba6bae7d7dd",
"public:assets/images/sponsor/wechat.png": "d5d5d5dad9d7dbd8cf",
"public:assets/images/sponsor/alipay.png": "8ebbde91bedf93c0e0",
"public:assets/images/ad/ad1.webp": "a1b8b68a8e918c8f93"
}
2 changes: 2 additions & 0 deletions src/i18n/i18nKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ enum I18nKey {
overlayOpacity = "overlayOpacity",
overlayBlur = "overlayBlur",
overlayCardOpacity = "overlayCardOpacity",
progressiveLoading = "progressiveLoading",
imageSettings = "imageSettings",

// 文章布局
postListLayout = "postListLayout",
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ export const en: Translation = {
[Key.overlayOpacity]: "Wallpaper Opacity",
[Key.overlayBlur]: "Background Blur",
[Key.overlayCardOpacity]: "Card Opacity",
[Key.progressiveLoading]: "Progressive Image Loading",
[Key.imageSettings]: "Image Settings",

// Post List Layout
[Key.postListLayout]: "Post List Layout",
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/languages/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ export const ja: Translation = {
[Key.overlayOpacity]: "壁紙の透明度",
[Key.overlayBlur]: "背景ぼかし",
[Key.overlayCardOpacity]: "カード透明度",
[Key.progressiveLoading]: "画像プログレッシブ読み込み",
[Key.imageSettings]: "画像設定",

// 投稿リストレイアウト
[Key.postListLayout]: "投稿リストレイアウト",
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/languages/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ export const ru: Translation = {
[Key.overlayOpacity]: "Прозрачность обоев",
[Key.overlayBlur]: "Размытие фона",
[Key.overlayCardOpacity]: "Прозрачность карточек",
[Key.progressiveLoading]: "Прогрессивная загрузка изображений",
[Key.imageSettings]: "Настройки изображений",

// Макет списка сообщений
[Key.postListLayout]: "Макет списка сообщений",
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/languages/zh_CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@ export const zh_CN: Translation = {
[Key.overlayOpacity]: "壁纸透明度",
[Key.overlayBlur]: "背景模糊度",
[Key.overlayCardOpacity]: "卡片透明度",
[Key.progressiveLoading]: "图片渐进式加载",
[Key.imageSettings]: "图片设置",

// 文章布局
[Key.postListLayout]: "文章布局",
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/languages/zh_TW.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ export const zh_TW: Translation = {
[Key.overlayOpacity]: "桌布透明度",
[Key.overlayBlur]: "背景模糊度",
[Key.overlayCardOpacity]: "卡片透明度",
[Key.progressiveLoading]: "圖片漸進式載入",
[Key.imageSettings]: "圖片設定",

// 文章佈局
[Key.postListLayout]: "文章佈局",
Expand Down
12 changes: 11 additions & 1 deletion src/layouts/Layout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,14 @@ const siteLang = lang.replace("_", "-");
wallpaperMode
);

// 初始化渐进式图片加载设置 - 在页面渲染前应用以避免闪烁
const progressiveStored = localStorage.getItem("progressiveLoadingEnabled");
const progressiveEnabled = progressiveStored === null ? "true" : progressiveStored;
document.documentElement.setAttribute(
"data-progressive-loading",
progressiveEnabled
);

// 立即执行函数来处理DOM
(function applyWallpaperMode() {
// 使用 requestAnimationFrame 确保在下一帧之前执行
Expand Down Expand Up @@ -1651,21 +1659,23 @@ function disableAnimation() {
}

// Initialize wallpaper mode
import { initWallpaperMode, initThemeListener } from "@/utils/setting-utils";
import { initWallpaperMode, initThemeListener, initProgressiveLoading } from "@/utils/setting-utils";
import { initIconLoader } from "@/utils/icon-loader";
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => {
updateMainGridCols();
updateSidebarComponentsVisibility();
initWallpaperMode();
initThemeListener();
initProgressiveLoading();
initIconLoader();
});
} else {
updateMainGridCols();
updateSidebarComponentsVisibility();
initWallpaperMode();
initThemeListener();
initProgressiveLoading();
initIconLoader();
}

Expand Down
18 changes: 17 additions & 1 deletion src/pages/gallery/[album].astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
import { getImage } from "astro:assets";
import { Icon } from "astro-icon/components";
import EncryptedContent from "@/components/features/EncryptedContent.astro";
import PhotoCard from "@/components/pages/gallery/PhotoCard.astro";
Expand Down Expand Up @@ -36,14 +37,29 @@ const columnWidth = galleryConfig.columnWidth || 240;
const coverLqipProps = cover
? getLqipProps(cover, undefined, true)
: { style: "" };

let tinyImgUrl = "";
if (siteConfig.post.generateTinyThumbnails && cover) {
try {
const tinyImg = await getImage({ src: cover, width: 32, quality: 30 });
tinyImgUrl = tinyImg.src;
} catch (e) {
console.error("Failed to generate tiny thumbnail:", e);
}
}
---

<MainGridLayout title={albumMeta.name} description={albumMeta.description || ""}>
<!-- 相册封面横幅 -->
<div class="w-full rounded-(--radius-large) overflow-hidden relative">
{cover ? (
<div class="relative w-full aspect-[3/1] min-h-[200px] max-h-[360px] overflow-hidden">
<div class="lqip-placeholder absolute inset-0 pointer-events-none" style={coverLqipProps.style} aria-hidden="true"></div>
<div
class="lqip-placeholder absolute inset-0 pointer-events-none"
style={`${coverLqipProps.style};${tinyImgUrl ? `--tiny-src: url(${tinyImgUrl})` : ''}`}
data-tiny-src={tinyImgUrl ? "true" : undefined}
aria-hidden="true"
></div>
<img
src={cover}
alt={albumMeta.name}
Expand Down
Loading