mirror of
https://github.com/Ad-closeNN/blog-fuwari.git
synced 2026-05-31 01:20:06 -04:00
feat(theme): 图片加载动画、Blur
This commit is contained in:
@@ -110,7 +110,7 @@ const { remarkPluginFrontmatter } = await render(entry);
|
|||||||
</Icon>
|
</Icon>
|
||||||
</div>
|
</div>
|
||||||
<ImageWrapper src={image} basePath={path.join("content/posts/", getDir(entry.id))} alt="Cover Image of the Post"
|
<ImageWrapper src={image} basePath={path.join("content/posts/", getDir(entry.id))} alt="Cover Image of the Post"
|
||||||
class="w-full h-full">
|
class="w-full h-full" loader>
|
||||||
</ImageWrapper>
|
</ImageWrapper>
|
||||||
</a>}
|
</a>}
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ interface Props {
|
|||||||
alt?: string;
|
alt?: string;
|
||||||
position?: string;
|
position?: string;
|
||||||
basePath?: string;
|
basePath?: string;
|
||||||
|
loader?: boolean;
|
||||||
|
blur?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
import { Image } from "astro:assets";
|
import { Image } from "astro:assets";
|
||||||
import { url } from "../../utils/url-utils";
|
import { url } from "../../utils/url-utils";
|
||||||
|
|
||||||
const { id, src, alt, position = "center", basePath = "/" } = Astro.props;
|
const { id, src, alt, position = "center", basePath = "/", loader = false, blur = false } = Astro.props;
|
||||||
const className = Astro.props.class;
|
const className = Astro.props.class;
|
||||||
|
|
||||||
const isLocal = !(
|
const isLocal = !(
|
||||||
@@ -51,7 +53,7 @@ const originalSrc = isLocal && img ? img.src : isPublic ? url(normalizedPublicSr
|
|||||||
const originalWidth = isLocal && img ? img.width : undefined;
|
const originalWidth = isLocal && img ? img.width : undefined;
|
||||||
const originalHeight = isLocal && img ? img.height : undefined;
|
const originalHeight = isLocal && img ? img.height : undefined;
|
||||||
---
|
---
|
||||||
<div id={id} class:list={[className, 'overflow-hidden relative']}>
|
<div id={id} class:list={[className, "overflow-hidden relative", loader && "image-loading-shell is-loading", blur && "image-blur-shell is-loading"]}>
|
||||||
<div class="transition absolute inset-0 dark:bg-black/10 bg-opacity-50 pointer-events-none"></div>
|
<div class="transition absolute inset-0 dark:bg-black/10 bg-opacity-50 pointer-events-none"></div>
|
||||||
{isLocal && img && <Image src={img} alt={alt || ""} class={imageClass} style={imageStyle} data-pswp-src={originalSrc} data-pswp-width={originalWidth} data-pswp-height={originalHeight} data-original-src={originalSrc}/>}
|
{isLocal && img && <Image src={img} alt={alt || ""} class={imageClass} style={imageStyle} data-pswp-src={originalSrc} data-pswp-width={originalWidth} data-pswp-height={originalHeight} data-original-src={originalSrc}/>}
|
||||||
{!isLocal && <img src={originalSrc} alt={alt || ""} class={imageClass} style={imageStyle} data-pswp-src={originalSrc} data-original-src={originalSrc}/>}
|
{!isLocal && <img src={originalSrc} alt={alt || ""} class={imageClass} style={imageStyle} data-pswp-src={originalSrc} data-original-src={originalSrc}/>}
|
||||||
|
|||||||
@@ -679,6 +679,88 @@ window.onresize = () => {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
type ImageLoaderState = {
|
||||||
|
pageLoadRegistered: boolean
|
||||||
|
swupHookRegistered: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageLoaderState = (((window as Window & { __blogImageLoader?: ImageLoaderState }).__blogImageLoader ??= {
|
||||||
|
pageLoadRegistered: false,
|
||||||
|
swupHookRegistered: false,
|
||||||
|
}) as ImageLoaderState)
|
||||||
|
|
||||||
|
function markImageLoaded(wrapper: HTMLElement) {
|
||||||
|
if (wrapper.classList.contains("image-blur-shell")) {
|
||||||
|
window.setTimeout(() => wrapper.classList.remove("is-loading"), 120)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wrapper.classList.remove("is-loading")
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindImageLoader(wrapper: HTMLElement) {
|
||||||
|
if (wrapper.dataset.imageLoadingBound === "true") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const image = wrapper.querySelector("img")
|
||||||
|
if (!(image instanceof HTMLImageElement)) {
|
||||||
|
markImageLoaded(wrapper)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper.dataset.imageLoadingBound = "true"
|
||||||
|
if (image.complete) {
|
||||||
|
markImageLoaded(wrapper)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
image.addEventListener("load", () => markImageLoaded(wrapper), { once: true })
|
||||||
|
image.addEventListener("error", () => markImageLoaded(wrapper), { once: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrapMarkdownImage(image: HTMLImageElement) {
|
||||||
|
if (image.closest(".image-loading-shell, .image-blur-shell") || !image.parentNode) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const wrapper = document.createElement("span")
|
||||||
|
wrapper.className = "image-loading-shell image-loading-shell--markdown is-loading"
|
||||||
|
image.parentNode.insertBefore(wrapper, image)
|
||||||
|
wrapper.appendChild(image)
|
||||||
|
}
|
||||||
|
|
||||||
|
function initImageLoaders() {
|
||||||
|
document.querySelectorAll<HTMLImageElement>(".custom-md img:not([data-image-loading-skip])").forEach(wrapMarkdownImage)
|
||||||
|
document.querySelectorAll<HTMLElement>(".image-loading-shell, .image-blur-shell").forEach(bindImageLoader)
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerImageLoaderSwupHook() {
|
||||||
|
if (imageLoaderState.swupHookRegistered || !window.swup?.hooks) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
window.swup.hooks.on("page:view", initImageLoaders)
|
||||||
|
imageLoaderState.swupHookRegistered = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === "loading") {
|
||||||
|
document.addEventListener("DOMContentLoaded", initImageLoaders, { once: true })
|
||||||
|
} else {
|
||||||
|
initImageLoaders()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!imageLoaderState.pageLoadRegistered) {
|
||||||
|
document.addEventListener("astro:page-load", initImageLoaders)
|
||||||
|
imageLoaderState.pageLoadRegistered = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.swup?.hooks) {
|
||||||
|
registerImageLoaderSwupHook()
|
||||||
|
} else {
|
||||||
|
document.addEventListener("swup:enable", registerImageLoaderSwupHook, { once: true })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import PhotoSwipeLightbox from "photoswipe/lightbox"
|
import PhotoSwipeLightbox from "photoswipe/lightbox"
|
||||||
import "photoswipe/style.css"
|
import "photoswipe/style.css"
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const mainPanelTop = siteConfig.banner.enable
|
|||||||
<!-- Banner -->
|
<!-- Banner -->
|
||||||
{siteConfig.banner.enable && <div id="banner-wrapper" class={`absolute z-10 w-full transition duration-700 overflow-hidden`} style={`top: -${BANNER_HEIGHT_EXTEND}vh`}>
|
{siteConfig.banner.enable && <div id="banner-wrapper" class={`absolute z-10 w-full transition duration-700 overflow-hidden`} style={`top: -${BANNER_HEIGHT_EXTEND}vh`}>
|
||||||
<ImageWrapper id="banner" alt="Banner image of the blog" class:list={["object-cover h-full transition duration-700 opacity-0 scale-105"]}
|
<ImageWrapper id="banner" alt="Banner image of the blog" class:list={["object-cover h-full transition duration-700 opacity-0 scale-105"]}
|
||||||
src={siteConfig.banner.src} position={siteConfig.banner.position}
|
src={siteConfig.banner.src} position={siteConfig.banner.position} blur
|
||||||
>
|
>
|
||||||
</ImageWrapper>
|
</ImageWrapper>
|
||||||
</div>}
|
</div>}
|
||||||
|
|||||||
@@ -373,12 +373,12 @@ const isOutdated = entry.data.outdated;
|
|||||||
<!-- always show cover as long as it has one -->
|
<!-- always show cover as long as it has one -->
|
||||||
<!-- 使用自制 showcover 控制器控制头图的出现 -->
|
<!-- 使用自制 showcover 控制器控制头图的出现 -->
|
||||||
{showcover && entry.data.image &&
|
{showcover && entry.data.image &&
|
||||||
<ImageWrapper id="post-cover" src={entry.data.image} basePath={path.join("content/posts/", getDir(entry.id))} class="mb-8 rounded-xl banner-container onload-animation"/>
|
<ImageWrapper id="post-cover" src={entry.data.image} basePath={path.join("content/posts/", getDir(entry.id))} class="mb-8 rounded-xl banner-container onload-animation" loader/>
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- 自制内部头图 customcover-->
|
<!-- 自制内部头图 customcover-->
|
||||||
{customcover &&
|
{customcover &&
|
||||||
<ImageWrapper id="post-cover" src={entry.data.customcover} basePath={path.join("content/posts/", getDir(entry.id))} class="mb-8 rounded-xl banner-container onload-animation"/>
|
<ImageWrapper id="post-cover" src={entry.data.customcover} basePath={path.join("content/posts/", getDir(entry.id))} class="mb-8 rounded-xl banner-container onload-animation" loader/>
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- 头图调试代码
|
<!-- 头图调试代码
|
||||||
|
|||||||
@@ -156,6 +156,50 @@
|
|||||||
@apply cursor-zoom-in
|
@apply cursor-zoom-in
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.image-loading-shell {
|
||||||
|
background: color-mix(in oklch, var(--card-bg) 92%, var(--btn-regular-bg) 8%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-loading-shell > img {
|
||||||
|
transition: opacity 0.28s ease, filter 0.55s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-blur-shell > img {
|
||||||
|
transition: filter 0.28s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-loading-shell.is-loading > img {
|
||||||
|
opacity: 0;
|
||||||
|
filter: blur(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-blur-shell.is-loading > img {
|
||||||
|
filter: blur(3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.image-loading-shell--markdown {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-loading-shell--markdown.is-loading {
|
||||||
|
min-height: 12rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-md .image-loading-shell--markdown {
|
||||||
|
margin-top: 2em;
|
||||||
|
margin-bottom: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-md .image-loading-shell--markdown img {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
::selection {
|
::selection {
|
||||||
background-color: var(--selection-bg);
|
background-color: var(--selection-bg);
|
||||||
color: inherit;
|
color: inherit;
|
||||||
|
|||||||
Reference in New Issue
Block a user