mirror of
https://github.com/Ad-closeNN/blog-fuwari.git
synced 2026-05-31 01:00:04 -04:00
feat(theme): 图片加载动画、Blur
This commit is contained in:
@@ -110,7 +110,7 @@ const { remarkPluginFrontmatter } = await render(entry);
|
||||
</Icon>
|
||||
</div>
|
||||
<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>
|
||||
</a>}
|
||||
|
||||
|
||||
@@ -8,12 +8,14 @@ interface Props {
|
||||
alt?: string;
|
||||
position?: string;
|
||||
basePath?: string;
|
||||
loader?: boolean;
|
||||
blur?: boolean;
|
||||
}
|
||||
|
||||
import { Image } from "astro:assets";
|
||||
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 isLocal = !(
|
||||
@@ -51,7 +53,7 @@ const originalSrc = isLocal && img ? img.src : isPublic ? url(normalizedPublicSr
|
||||
const originalWidth = isLocal && img ? img.width : 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>
|
||||
{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}/>}
|
||||
|
||||
@@ -679,6 +679,88 @@ window.onresize = () => {
|
||||
}
|
||||
</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>
|
||||
import PhotoSwipeLightbox from "photoswipe/lightbox"
|
||||
import "photoswipe/style.css"
|
||||
|
||||
@@ -54,7 +54,7 @@ const mainPanelTop = siteConfig.banner.enable
|
||||
<!-- 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`}>
|
||||
<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>
|
||||
</div>}
|
||||
|
||||
@@ -373,12 +373,12 @@ const isOutdated = entry.data.outdated;
|
||||
<!-- always show cover as long as it has one -->
|
||||
<!-- 使用自制 showcover 控制器控制头图的出现 -->
|
||||
{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 &&
|
||||
<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
|
||||
}
|
||||
|
||||
.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 {
|
||||
background-color: var(--selection-bg);
|
||||
color: inherit;
|
||||
|
||||
Reference in New Issue
Block a user