import React, { forwardRef, memo, useMemo } from 'react' import { Canvas, Image as SkiaImage, useImage } from '@shopify/react-native-skia' import type { StyleProp, ViewStyle } from 'react-native' import tw from 'twrnc' interface ImgProps { className?: string src?: string | number /** 画布宽度(同时也作为压缩宽度) */ width?: number /** 画布高度,不传则等于 width */ height?: number isWebP?: boolean errorSource?: string | number isCompression?: boolean /** 自定义缓存键,用于需要重定向的 URL */ cacheKey?: string style?: StyleProp [key: string]: any } const Img = forwardRef((props, ref) => { const { className = '', style = {}, src, errorSource, source: propSource, cacheKey, width = 256, height, isCompression = false, isWebP = true, ...rest } = props const imageStyle = tw`${className}` const canvasWidth = useMemo(() => { if (typeof (style as ViewStyle)?.width === 'number') return (style as ViewStyle).width return width }, [style, width]) const canvasHeight = useMemo(() => { if (typeof (style as ViewStyle)?.height === 'number') return (style as ViewStyle).height if (height != null) return height return width }, [style, width, height]) // 静态图全部压缩jpg const compressionUrl = useMemo(() => { const f = isWebP ? 'webp' : 'jpg' return `https://bowong.cc/cdn-cgi/image/width=${width},quality=75,format=${f}/${src}` }, [width, isWebP, src]) // 判断是否为网络图片 const isNetworkImage = (uri: string | number): boolean => { if (typeof uri !== 'string') return false return uri.startsWith('http://') || uri.startsWith('https://') } // 构建图片源 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 : src // console.log('finalUrl-------------', finalUrl) return { uri: finalUrl, cacheKey: cacheKey || finalUrl, } } else { // 本地文件路径 return { uri: src } } } }, [src, propSource, cacheKey, isCompression, compressionUrl]) // Skia useImage 仅接受 require、URL 或命名资源 const skiaSource = useMemo(() => { if (imageSource == null) return undefined if (typeof imageSource === 'number') return imageSource if (typeof imageSource === 'string') return imageSource if (typeof (imageSource as any).uri === 'string') return (imageSource as any).uri as string return undefined }, [imageSource]) const skImage = useImage(skiaSource as any) if (!skImage) { // 图片还在加载中或加载失败时,暂时不渲染内容 return null } return ( ) }) Img.displayName = 'Img' export default memo(Img)