React Hooks 实现图片懒加载useLazyLoad

定义useLazyLoad

思路: 判断图片在视口内就加载,即: 元素距离页面顶部的距离offsetTop < 滚动条高度scrollTop + 视口高clientHeight

import { useCallback, useEffect, useState } from 'react'
import { ImgDOMListType, ImageListType } from '../components/LazyLoadDemo'
import useThrottle from './useDebounce'

interface LazyLoadPropsType {
    domList: ImgDOMListType,
    imgList: ImageListType,
    throttle: number
}

const useLazyLoad = ({ domList, imgList, throttle }: LazyLoadPropsType) => {

    const [scrollCount, setScrollCount] = useState(0)

    // 节流
    const loadImg = useThrottle(() => {
        domList.forEach((el, i) => {
            // 已经加载的无需继续加载
            if (!el || imgList[i].loaded) return
            // 加载条件:判断元素在视口内,即图片距离页面顶部的距离 offsetTop < 滚动条高度+视口高
            if (el.offsetTop < document.documentElement.scrollTop + document.documentElement.clientHeight) {
                el.src = el.dataset.src as string
                imgList[i].loaded = true
            }
        })
    }, throttle, [domList, imgList, throttle])

    const beginLoad = useCallback(() => setScrollCount(v => v + 1), [setScrollCount])

    // 监听scroll事件
    useEffect(() => {
        document.addEventListener('scroll', beginLoad)
        return () => document.removeEventListener('scroll', beginLoad)
    }, [beginLoad])

    // 开始计算懒加载图片位置
    useEffect(() => {
        loadImg()
    }, [loadImg, scrollCount])

}

export default useLazyLoad

测试用例

import { useRef } from "react"
import useLazyLoad from "../hooks/useLazyLoad"

import './LazyLoadDemo.css'

export type ImageListType = Array<{
    id: number | string,
    src: string,
    loadingSrc: string,
    loaded: boolean
}>
export type ImgDOMListType = Array<HTMLImageElement | null>

const loadingSrc = 'https://www.maojiemao.com/public/svg/gen1.png'
const src = 'https://www.maojiemao.com/public/svg/gen2.png'

const imgList: ImageListType = Array.from({ length: 30 }).map((v, i) => ({
    id: i,
    src,
    loadingSrc,
    loaded: false
}))

const LazyLoadDemo: React.FC = () => {
    const domRef = useRef<ImgDOMListType>([])
    useLazyLoad({ domList: domRef.current, imgList, throttle: 200 })

    return (
        <div className="wrapper">
            {
                imgList.map((v, i: number) =>
                    <img
                        key={i}
                        ref={(el) => (domRef.current[i] = el)}
                        src={v.loadingSrc}
                        data-src={v.src}
                        alt=""
                        className="item" />
                )
            }
        </div>
    )
}

export default LazyLoadDemo
posted @ 2022-06-21 00:20  IslandZzzz  阅读(956)  评论(0编辑  收藏  举报