@nutui/nutui-react
Version:
京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序
126 lines (125 loc) • 4.6 kB
JavaScript
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
};