import React, { useEffect, useRef, useState } from 'react' import useTheme from '../use-theme' import ImageSkeleton from './image.skeleton' import ImageBrowser from './image-browser' import useRealShape from '../utils/use-real-shape' import useCurrentState from '../utils/use-current-state' import useResize from '../utils/use-resize' interface Props { src: string disableAutoResize?: boolean disableSkeleton?: boolean width?: number height?: number className?: string scale?: string maxDelay?: number } const defaultProps = { disableSkeleton: false, disableAutoResize: false, className: '', scale: '100%', maxDelay: 3000 } type NativeAttrs = Omit, keyof Props> export type ImageProps = Props & typeof defaultProps & NativeAttrs const Image: React.FC = ({ src, width, height, disableSkeleton, className, scale, maxDelay, disableAutoResize, ...props }) => { const showAnimation = !disableSkeleton && width && height const w = width ? `${width}px` : 'auto' const h = height ? `${height}px` : 'auto' const theme = useTheme() const [loading, setLoading] = useState(true) const [showSkeleton, setShowSkeleton] = useState(true) const [zoomHeight, setZoomHeight, zoomHeightRef] = useCurrentState(h) const imageRef = useRef(null) const [shape, updateShape] = useRealShape(imageRef) const imageLoaded = () => { if (!showAnimation) return setLoading(false) } useEffect(() => { if (!showAnimation) return if (!imageRef.current) return if (imageRef.current.complete) { setLoading(false) setShowSkeleton(false) } }) useEffect(() => { const timer = setTimeout(() => { if (showAnimation) { setShowSkeleton(false) } clearTimeout(timer) }, maxDelay) return () => clearTimeout(timer) }, [loading]) /** * On mobile devices, the render witdth may be less than CSS width value. * If the image is scaled, set the height manually. * This is to ensure the aspect ratio of the image. * * If the image is auto width, ignore all. */ useEffect(() => { if (disableAutoResize) return const notLoaded = shape.width === 0 const isAutoZoom = zoomHeightRef.current === 'auto' if (notLoaded || !width || !height) return if (shape.width < width) { !isAutoZoom && setZoomHeight('auto') } else { isAutoZoom && setZoomHeight(h) } }, [shape, width]) useResize(() => { if (disableAutoResize) return updateShape() }) return (
{showSkeleton && showAnimation && }
) } type MemoImageComponent

= React.NamedExoticComponent

& { Browser: typeof ImageBrowser } type ComponentProps = Partial & Omit & NativeAttrs Image.defaultProps = defaultProps export default React.memo(Image) as MemoImageComponent