UNPKG

@nutui/nutui-react

Version:

京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序

126 lines (125 loc) 4.6 kB
import React__default, { useState, useRef, useEffect, useCallback } from "react"; import { ImageError, Image as Image$1 } from "@nutui/icons-react"; import classNames from "classnames"; import { C as ComponentDefaults } from "./typings.js"; import { p as pxCheck } from "./px-check.js"; const defaultProps = Object.assign(Object.assign({}, ComponentDefaults), { fit: "fill", position: "center", alt: "", width: "", height: "", error: true, loading: true, lazy: false }); const classPrefix = "nut-image"; const Image = (props) => { const { className, style, src, fit, position, alt, width, height, radius, error, loading, lazy, draggable, onClick, onLoad, onError } = Object.assign(Object.assign({}, defaultProps), props); const [innerLoading, setInnerLoading] = useState(false); const [isError, setIsError] = useState(false); const [complete, setComplete] = useState(false); const imgRef = useRef(null); useEffect(() => { if (imgRef.current && imgRef.current.complete && !lazy) { if (imgRef.current.naturalHeight === 0) { handleError(); } else { handleLoad(); } } else { setInnerLoading(true); } }, [imgRef]); useEffect(() => { setComplete(false); }, [src]); const handleLoad = () => { if (!complete) { setIsError(false); setInnerLoading(false); onLoad && onLoad(); setComplete(true); } }; const handleError = () => { if (!complete) { setIsError(true); setInnerLoading(false); onError && onError(); setComplete(true); } }; const containerStyle = Object.assign({ height: height ? pxCheck(height) : "", width: width ? pxCheck(width) : "", overflow: radius !== void 0 && radius !== null ? "hidden" : "", borderRadius: radius !== void 0 && radius !== null ? pxCheck(radius) : "" }, style); const imgStyle = Object.assign({ objectFit: fit, objectPosition: position }, style); const observer = useRef(null); const initObserver = () => { const options = { threshold: [0], // 交会处 rootMargin: "0px" // 对视口进行收缩和扩张 }; observer.current = new IntersectionObserver((entires, self) => { entires.forEach((item) => { if (item.isIntersecting) { setTimeout(() => { const img = item.target; if (img.dataset.src) { img.src = img.dataset.src; img.removeAttribute("data-src"); } resetObserver(); }, 300); } }); }, options); observer.current.observe(imgRef.current); }; const resetObserver = () => { observer.current.disconnect && observer.current.disconnect(); }; useEffect(() => { lazy && initObserver(); return () => { lazy && resetObserver(); }; }, [lazy]); const imageClick = (event) => { onClick && onClick(event); }; const renderErrorImg = useCallback(() => { if (!isError) return null; if (typeof error === "boolean" && error === true && !innerLoading) { return React__default.createElement( "div", { className: "nut-img-error" }, React__default.createElement(ImageError, null) ); } if (React__default.isValidElement(error) && !innerLoading) { return React__default.createElement("div", { className: "nut-img-error" }, error); } return null; }, [error, isError]); const renderLoading = useCallback(() => { if (!loading) return null; if (typeof loading === "boolean" && loading === true && innerLoading) { return React__default.createElement( "div", { className: "nut-img-loading" }, React__default.createElement(Image$1, null) ); } if (React__default.isValidElement(loading) && innerLoading) { return React__default.createElement("div", { className: "nut-img-loading" }, loading); } return null; }, [loading, innerLoading]); return React__default.createElement( "div", { className: classNames(classPrefix, className), style: containerStyle, onClick: (e) => { imageClick(e); } }, lazy ? React__default.createElement("img", { ref: imgRef, className: "nut-img lazyload", style: imgStyle, "data-src": src, alt, loading: "lazy", onLoad: handleLoad, onError: handleError, draggable }) : React__default.createElement("img", { ref: imgRef, className: "nut-img", style: imgStyle, src, alt, onLoad: handleLoad, onError: handleError, draggable }), renderLoading(), renderErrorImg() ); }; Image.displayName = "NutImage"; export { Image as default };