import React, { forwardRef, memo, useEffect, useMemo, useState } from 'react' import { Image as ExpoImage, ImageProps as ExpoImageProps } from 'expo-image' import { TouchableOpacity, TouchableOpacityProps, View } from 'react-native' interface ImgProps extends ExpoImageProps { onClick?: () => void touchProps?: TouchableOpacityProps className?: string src?: string | number errorSource?: string | number /** 图片宽度,用于 CDN 压缩 */ width?: number /** 是否使用 WebP 格式(默认 true) */ isWebP?: boolean /** 是否启用 CDN 压缩(默认 false) */ isCompression?: boolean /** 自定义缓存键,用于需要重定向的 URL */ cacheKey?: string /** 占位图 URL,在真实图片加载完成前显示 */ placeholderSrc?: string } const Img = forwardRef((props, ref) => { const { onClick, touchProps = {}, className = '', style, src, errorSource, source: propSource, width = 256, isWebP = true, isCompression = false, cacheKey, placeholderSrc, onLoad, ...reset } = props const [isLoaded, setIsLoaded] = useState(false) useEffect(() => { setIsLoaded(false) }, [src]) // 判断是否为网络图片 const isNetworkImage = (uri: string | number): boolean => { if (typeof uri === 'number') return false return uri.startsWith('http://') || uri.startsWith('https://') } // CDN 压缩 URL(使用 Cloudflare 图片优化服务) const compressionUrl = useMemo((): string | undefined => { if (!src || typeof src !== 'string' || !isNetworkImage(src)) return undefined const format = isWebP ? 'webp' : 'jpg' return `https://bowong.cc/cdn-cgi/image/width=${width},quality=75,format=${format}/${src}` }, [width, isWebP, src]) // 构建图片源 const imageSource = useMemo(() => { // 如果提供了source属性,优先使用 if (propSource) return propSource if (!src) return undefined if (typeof src === 'number') { // 本地图片资源(require导入的资源ID) return src } else { // 网络图片或本地文件路径 if (isNetworkImage(src)) { const finalUrl = (isCompression && compressionUrl) ? compressionUrl : src return { uri: finalUrl, cacheKey: cacheKey || finalUrl, } } else { // 本地文件路径 return { uri: src } } } }, [src, propSource, cacheKey, isCompression, compressionUrl]) const handleLoad = (e: any) => { setIsLoaded(true) onLoad?.(e) } const handlePress = () => { onClick && onClick() } // 渲染图片内容 const renderImage = () => { // 无占位图时直接返回原图 if (!placeholderSrc) { return ( ) } // 有占位图时使用层叠布局 return ( {/* 占位图层 - 加载完成后隐藏 */} {/* 真实图片层 */} ) } if (onClick) { return ( {renderImage()} ) } return renderImage() }) Img.displayName = 'Img' export default memo(Img)