Image component lazy behavior

This commit is contained in:
tmshv committed 2020-03-03 06:49:18 +03:00
1 parent 8f54454d32
commit 11ff066e6c
4 files changed
+79 -17

No files matched your search

-3
View File
@@ -67,9 +67,6 @@ const Page: NextPage<Props> = props => {
srcSet={srcSet}
style={{
marginBottom: 'var(--size-l)',
}}
imgStyle={{
width: '100%',
borderRadius: 'var(--radius)',
}}
/>
+2 -2
View File
@@ -1,6 +1,7 @@
import './styles.css'
import cx from 'classnames'
import Link from 'next/link'
import { Image } from '../Image'
export type CardLayout = 'simple' | 'featured'
export type CardProps = {
@@ -19,8 +20,7 @@ export const Card: React.FC<CardProps> = props => (
<Link href={props.href}>
<a>
<div className={'card-image'}>
<img
className={cx({ opa: false })}
<Image
alt={props.img.alt}
src={props.img.src}
srcSet={props.img.srcSet}
+60 -10
View File
@@ -1,23 +1,73 @@
import './styles.css'
import { memo } from 'react'
import { memo, useRef, useState, useEffect, useCallback } from 'react'
export type ImageProps = {
style?: React.CSSProperties
imgStyle?: React.CSSProperties
src: string
alt?: string
srcSet?: string
}
export const Image: React.FC<ImageProps> = memo(props => {
const [imageSrc, setImageSrc] = useState<[string, string]>([null, null])
const ref = useRef<HTMLImageElement>()
const onLoad = useCallback((event) => {
event.target.classList.add('loaded')
event.target.classList.remove('pending')
}, [])
useEffect(() => {
if (ref.current) {
ref.current.classList.add('pending')
}
})
useEffect(() => {
let observer: IntersectionObserver
let didCancel = false
if (ref.current && imageSrc[0] !== props.src) {
if (IntersectionObserver) {
observer = new IntersectionObserver(
entries => {
entries.forEach(entry => {
if (
!didCancel &&
(entry.intersectionRatio > 0 || entry.isIntersecting)
) {
setImageSrc([props.src, props.srcSet])
observer.unobserve(ref.current)
}
})
},
{
threshold: 0.01,
rootMargin: "75%",
}
)
observer.observe(ref.current)
} else {
// Old browsers fallback
setImageSrc([props.src, props.srcSet])
}
}
return () => {
didCancel = true
if (observer && observer.unobserve) {
observer.unobserve(ref.current)
}
}
}, [props.src, props.srcSet, imageSrc, ref])
return (
<picture className={'image'} style={props.style}>
<img
style={props.imgStyle}
alt={props.alt}
src={props.src}
srcSet={props.srcSet}
/>
</picture>
<img
ref={ref}
onLoad={onLoad}
className={'image'}
style={props.style}
alt={props.alt}
src={imageSrc[0]}
srcSet={imageSrc[1]}
/>
)
})
+17 -2
View File
@@ -1,7 +1,22 @@
@keyframes loaded {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.image {
width: 100%;
display: block;
}
.image img {
display: block;
.image.pending {
opacity: 0;
}
.image.loaded {
animation: loaded 250ms ease-in-out;
opacity: 1;
}