feat: 图片lazy loading
This commit is contained in:
0
src/components/AspectRatio.astro
Normal file
0
src/components/AspectRatio.astro
Normal file
131
src/components/LazyImage.astro
Normal file
131
src/components/LazyImage.astro
Normal file
@@ -0,0 +1,131 @@
|
||||
---
|
||||
interface Props {
|
||||
src: string;
|
||||
alt?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
src,
|
||||
alt = "",
|
||||
width,
|
||||
height,
|
||||
class: className = ""
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<div
|
||||
class={`lazy-image ${className}`}
|
||||
data-src={src}
|
||||
style={`aspect-ratio:${width}/${height}`}
|
||||
>
|
||||
<div class="placeholder"></div>
|
||||
|
||||
<img
|
||||
class="real-image"
|
||||
alt={alt}
|
||||
width={width}
|
||||
height={height}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<style>
|
||||
.lazy-image {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.lazy-image img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: #f3f4f6;
|
||||
background-image: linear-gradient(
|
||||
90deg,
|
||||
#f3f4f6 0%,
|
||||
#e5e7eb 50%,
|
||||
#f3f4f6 100%
|
||||
);
|
||||
background-size: 200% 100%;
|
||||
animation: loading 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
from {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
|
||||
to {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
}
|
||||
|
||||
.real-image {
|
||||
opacity: 0;
|
||||
transition: opacity .35s ease;
|
||||
}
|
||||
|
||||
.real-image.loaded {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<script>
|
||||
function initLazyImages() {
|
||||
const images = document.querySelectorAll(".lazy-image");
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
entries => {
|
||||
entries.forEach(entry => {
|
||||
|
||||
if (!entry.isIntersecting) return;
|
||||
|
||||
const container = entry.target as HTMLElement;
|
||||
const img = container.querySelector(
|
||||
".real-image"
|
||||
) as HTMLImageElement;
|
||||
|
||||
const src = container.dataset.src;
|
||||
|
||||
if (!src) return;
|
||||
|
||||
img.src = src;
|
||||
|
||||
img.onload = () => {
|
||||
img.classList.add("loaded");
|
||||
|
||||
const placeholder =
|
||||
container.querySelector(".placeholder");
|
||||
|
||||
placeholder?.remove();
|
||||
};
|
||||
|
||||
observer.unobserve(container);
|
||||
});
|
||||
},
|
||||
{
|
||||
rootMargin: "300px"
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
images.forEach(img => observer.observe(img));
|
||||
}
|
||||
|
||||
|
||||
initLazyImages();
|
||||
</script>
|
||||
@@ -4,6 +4,7 @@ import Folder from "@/assets/icon/folder.png?url";
|
||||
import Calendar from "@/assets/icon/calendar.png?url";
|
||||
import type {Post} from "@/types/post";
|
||||
import {formatDatetime} from "@/lib/format.ts";
|
||||
import LazyImage from "@/components/LazyImage.astro";
|
||||
|
||||
interface Props {
|
||||
posts: Post[]
|
||||
@@ -23,7 +24,7 @@ posts.map((post) => {
|
||||
<a href={`/post/${post.slug}`}>
|
||||
<article class="post-card">
|
||||
<div class="post-cover">
|
||||
<img src={post.cover} alt={post.title} loading="lazy"/>
|
||||
<LazyImage src={post.cover} alt={post.title} />
|
||||
</div>
|
||||
<div class="post-body">
|
||||
<div class="post-meta">
|
||||
@@ -112,12 +113,6 @@ posts.map((post) => {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.post-cover img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.post-body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
@@ -16,7 +16,7 @@ const { list, total } = await http.get<ApiPageResponse<Post>>("posts", {
|
||||
const totalPage = Math.ceil(total / metadata.postPageSize);
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<Layout description="天若有情天亦老,人间正道是沧桑。" title="博客首页">
|
||||
<IndexView
|
||||
posts={list}
|
||||
page={1}
|
||||
|
||||
@@ -27,7 +27,7 @@ if (totalPage < Number(page)) {
|
||||
}
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<Layout description="天若有情天亦老,人间正道是沧桑。" title="博客首页">
|
||||
<IndexView
|
||||
posts={list}
|
||||
page={Number(page)}
|
||||
|
||||
Reference in New Issue
Block a user