mirror of
https://github.com/DefectingCat/DefectingCat.github.io
synced 2025-07-16 09:11:38 +00:00
Add image lazy load hook
* remove fallback src on avatar * remove PostImage component * update README * add iframe lazy load on post page
This commit is contained in:
23
README.md
23
README.md
@ -1,7 +1,28 @@
|
||||
## 小破站
|
||||
|
||||
一个个人博客,Powered by [Next.js](https://nextjs.org/).
|
||||
一个个人博客✍,[RUA](https://blog.rua.plus/)。
|
||||
|
||||
## 施工中
|
||||
|
||||
### 独立页面
|
||||
|
||||
- [x] 归档页面
|
||||
- [x] 关于页面
|
||||
- [ ] PGP页面
|
||||
- [ ] 其他
|
||||
|
||||
### 其他功能
|
||||
|
||||
- [x] 图片懒加载
|
||||
- [x] 暗色模式
|
||||
- [x] 复制代码块内容
|
||||
- [x] NProgress
|
||||
- [ ] 暗色模式代码高亮
|
||||
|
||||
## ❤️
|
||||
|
||||
* Markdown 样式:[https://github.com/mingluosunyi/typora-theme-ava-diana](https://github.com/mingluosunyi/typora-theme-ava-diana)
|
||||
* Markdown processer:[unifiedjs](https://github.com/unifiedjs/unified)
|
||||
* Host on [Vercel](https://vercel.com/)
|
||||
|
||||
Powered by [Next.js](https://nextjs.org/) ❤️ [Chakra UI](https://chakra-ui.com/).
|
@ -6,6 +6,7 @@ import { useDispatch } from 'react-redux';
|
||||
import { setFromPath } from '../features/router/routerSlice';
|
||||
import { useRouter } from 'next/router';
|
||||
import useGetColors from '../lib/hooks/useGetColors';
|
||||
import useLazyLoad from '../lib/hooks/useLazyload';
|
||||
|
||||
interface Props {
|
||||
post: AllPostsData;
|
||||
@ -15,6 +16,8 @@ const ArchiveCard: FC<Props> = ({ post }) => {
|
||||
const dispatch = useDispatch();
|
||||
const router = useRouter();
|
||||
|
||||
const { initSrc, blur, targetRef } = useLazyLoad(post.index_img);
|
||||
|
||||
const { borderColor, headingColor } = useGetColors();
|
||||
|
||||
const goToPost: MouseEventHandler<HTMLAnchorElement> = (e) => {
|
||||
@ -50,7 +53,10 @@ const ArchiveCard: FC<Props> = ({ post }) => {
|
||||
</Box>
|
||||
{post.index_img ? (
|
||||
<Image
|
||||
src={post.index_img}
|
||||
ref={targetRef}
|
||||
transitionDuration="slower"
|
||||
filter={blur}
|
||||
src={initSrc}
|
||||
alt="Post image"
|
||||
w="50px"
|
||||
h="50px"
|
||||
|
@ -122,7 +122,6 @@ const NavBar: FC = () => {
|
||||
boxShadow={'card'}
|
||||
objectFit={'cover'}
|
||||
alt="Avatar"
|
||||
fallbackSrc="https://via.placeholder.com/150"
|
||||
/>
|
||||
{/* emoji on avatar */}
|
||||
<Flex
|
||||
|
@ -1,63 +0,0 @@
|
||||
import { FC, useEffect, useRef, useState } from 'react';
|
||||
import Zoom from 'react-medium-image-zoom';
|
||||
import { Image } from '@chakra-ui/react';
|
||||
import placeholder from '../../public/images/img/placeholder-600x500.webp';
|
||||
|
||||
interface Props {
|
||||
isLargerThan768: boolean;
|
||||
src: string;
|
||||
}
|
||||
|
||||
const PostImage: FC<Props> = ({ isLargerThan768, src }) => {
|
||||
const imgRef = useRef<HTMLImageElement>(null);
|
||||
const [blur, setBlur] = useState('blur(20px)');
|
||||
|
||||
const cleanBlur = () => {
|
||||
setBlur('unset');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Lazy load images when they entry the view port.
|
||||
const observer = new IntersectionObserver((entries, observer) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
if (imgRef.current) {
|
||||
imgRef.current.src = src;
|
||||
observer.unobserve(imgRef.current);
|
||||
imgRef.current.addEventListener('load', cleanBlur);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
imgRef.current && observer.observe(imgRef.current);
|
||||
|
||||
const cleanRef = imgRef.current;
|
||||
// Clean up the images event listener.
|
||||
return () => {
|
||||
cleanRef && cleanRef.removeEventListener('load', cleanBlur);
|
||||
};
|
||||
}, [src]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Zoom
|
||||
wrapElement="a"
|
||||
wrapStyle={{ width: '100%' }}
|
||||
zoomMargin={isLargerThan768 ? 300 : 0}
|
||||
>
|
||||
<Image
|
||||
ref={imgRef}
|
||||
borderRadius="10px"
|
||||
src={placeholder.blurDataURL}
|
||||
w="100%"
|
||||
filter={blur}
|
||||
transitionDuration="slower"
|
||||
alt="Post image"
|
||||
/>
|
||||
</Zoom>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PostImage;
|
49
lib/hooks/useLazyload.ts
Normal file
49
lib/hooks/useLazyload.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
const useLazyLoad = (src: string) => {
|
||||
const targetRef = useRef(null);
|
||||
const [blur, setBlur] = useState('blur(20px)');
|
||||
|
||||
const [initSrc, setInitSrc] = useState(
|
||||
'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='
|
||||
);
|
||||
|
||||
const cleanBlur = () => {
|
||||
setBlur('unset');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Lazy load images when they entry the view port.
|
||||
const observer = new IntersectionObserver((entries, observer) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
if (targetRef.current) {
|
||||
setInitSrc(src);
|
||||
observer.unobserve(targetRef.current);
|
||||
(targetRef.current as HTMLElement).addEventListener(
|
||||
'load',
|
||||
cleanBlur
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
targetRef.current && observer.observe(targetRef.current);
|
||||
|
||||
const cleanRef = targetRef.current;
|
||||
// Clean up the images event listener.
|
||||
return () => {
|
||||
cleanRef &&
|
||||
(cleanRef as HTMLElement).removeEventListener('load', cleanBlur);
|
||||
};
|
||||
}, [src]);
|
||||
|
||||
return {
|
||||
initSrc,
|
||||
blur,
|
||||
targetRef,
|
||||
};
|
||||
};
|
||||
|
||||
export default useLazyLoad;
|
@ -1,3 +1,15 @@
|
||||
import {
|
||||
Box,
|
||||
Image,
|
||||
Heading,
|
||||
Flex,
|
||||
Icon,
|
||||
Link,
|
||||
Button,
|
||||
Tag,
|
||||
useMediaQuery,
|
||||
AspectRatio,
|
||||
} from '@chakra-ui/react';
|
||||
import { getAllPostSlugs, getPostData } from '../../lib/posts';
|
||||
import { GetStaticProps, InferGetStaticPropsType } from 'next';
|
||||
import Head from 'next/head';
|
||||
@ -11,17 +23,6 @@ import remarkGfm from 'remark-gfm';
|
||||
import rehypeHighlight from 'rehype-highlight';
|
||||
import { unified } from 'unified';
|
||||
import { createElement, Fragment } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Image,
|
||||
Heading,
|
||||
Flex,
|
||||
Icon,
|
||||
Link,
|
||||
Button,
|
||||
Tag,
|
||||
useMediaQuery,
|
||||
} from '@chakra-ui/react';
|
||||
import 'highlight.js/styles/github.css';
|
||||
// import 'highlight.js/styles/github-dark.css';
|
||||
import xml from 'highlight.js/lib/languages/xml';
|
||||
@ -36,7 +37,8 @@ import { RootState } from '../../app/store';
|
||||
import { cleanFromPath } from '../../features/router/routerSlice';
|
||||
import CopyButton from '../../components/post/CopyButton';
|
||||
import useGetColors from '../../lib/hooks/useGetColors';
|
||||
import PostImage from '../../components/post/PostImage';
|
||||
import Zoom from 'react-medium-image-zoom';
|
||||
import useLazyLoad from '../../lib/hooks/useLazyload';
|
||||
import { useAppSelector, useAppDispatch } from '../../app/hooks';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
@ -88,8 +90,23 @@ const Post = ({ postData }: InferGetStaticPropsType<typeof getStaticProps>) => {
|
||||
createElement,
|
||||
components: {
|
||||
img: (props: any) => {
|
||||
const { initSrc, blur, targetRef } = useLazyLoad(props.src);
|
||||
return (
|
||||
<PostImage isLargerThan768={isLargerThan768} src={props.src} />
|
||||
<Zoom
|
||||
wrapElement="a"
|
||||
wrapStyle={{ width: '100%' }}
|
||||
zoomMargin={isLargerThan768 ? 300 : 0}
|
||||
>
|
||||
<Image
|
||||
ref={targetRef}
|
||||
borderRadius="10px"
|
||||
src={initSrc}
|
||||
w="100%"
|
||||
filter={blur}
|
||||
transitionDuration="slower"
|
||||
alt="Post image"
|
||||
/>
|
||||
</Zoom>
|
||||
);
|
||||
},
|
||||
a: (props: any) => {
|
||||
@ -100,6 +117,21 @@ const Post = ({ postData }: InferGetStaticPropsType<typeof getStaticProps>) => {
|
||||
);
|
||||
},
|
||||
pre: CopyButton,
|
||||
iframe: (props: any) => {
|
||||
const { initSrc, blur, targetRef } = useLazyLoad(props.src);
|
||||
return (
|
||||
<AspectRatio
|
||||
filter={blur}
|
||||
w="100%"
|
||||
transitionDuration="slower"
|
||||
ratio={16 / 9}
|
||||
>
|
||||
<Box as="iframe" src={initSrc} ref={targetRef}>
|
||||
{props.child}
|
||||
</Box>
|
||||
</AspectRatio>
|
||||
);
|
||||
},
|
||||
},
|
||||
Fragment,
|
||||
});
|
||||
|
Reference in New Issue
Block a user