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:
DefectingCat
2021-11-25 22:55:05 +08:00
parent 4db3db2a24
commit 20eaca66d2
6 changed files with 123 additions and 79 deletions

View File

@ -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/).

View File

@ -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"

View File

@ -122,7 +122,6 @@ const NavBar: FC = () => {
boxShadow={'card'}
objectFit={'cover'}
alt="Avatar"
fallbackSrc="https://via.placeholder.com/150"
/>
{/* emoji on avatar */}
<Flex

View File

@ -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
View 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;

View File

@ -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,
});