113 lines
3.0 KiB
TypeScript
113 lines
3.0 KiB
TypeScript
import { Image as ExpoImage, type ImageProps as ExpoImageProps } from 'expo-image'
|
||
import React, { forwardRef, useMemo } from 'react'
|
||
import { TouchableOpacity, type TouchableOpacityProps } from 'react-native'
|
||
import tw from 'twrnc'
|
||
|
||
interface ImgProps extends ExpoImageProps {
|
||
onClick?: () => void
|
||
touchProps?: TouchableOpacityProps
|
||
className?: string
|
||
src?: string | number
|
||
errorSource?: string | number
|
||
/** 自定义缓存键,用于需要重定向的 URL */
|
||
cacheKey?: string
|
||
}
|
||
|
||
const Img = forwardRef<ExpoImage, ImgProps>((props, ref) => {
|
||
const {
|
||
onClick,
|
||
touchProps = {},
|
||
className = '',
|
||
style = {},
|
||
src,
|
||
errorSource,
|
||
source: propSource,
|
||
cacheKey,
|
||
...reset
|
||
} = props
|
||
|
||
const imageStyle = tw`${className}`
|
||
|
||
// 判断是否为网络图片
|
||
const isNetworkImage = (uri: string | number): boolean => {
|
||
if (typeof uri !== 'string') return false
|
||
return uri.startsWith('http://') || uri.startsWith('https://')
|
||
}
|
||
|
||
// 自动从 URL 提取稳定的缓存键
|
||
const extractCacheKey = (url: string): string => {
|
||
try {
|
||
const urlObj = new URL(url)
|
||
// 使用路径部分作为缓存键,忽略查询参数(token、签名等)
|
||
return urlObj.pathname
|
||
} catch {
|
||
// URL 解析失败时使用原始 URL
|
||
return url
|
||
}
|
||
}
|
||
|
||
// 构建图片源
|
||
const imageSource = useMemo(() => {
|
||
// 如果提供了source属性,优先使用
|
||
if (propSource) return propSource
|
||
|
||
if (!src) return undefined
|
||
|
||
if (typeof src === 'number') {
|
||
// 本地图片资源(require导入的资源ID)
|
||
return src
|
||
} else {
|
||
// 网络图片或本地文件路径
|
||
if (isNetworkImage(src)) {
|
||
// 自动提取缓存键:优先使用传入的 cacheKey,否则从 URL 路径自动提取
|
||
const autoCacheKey = cacheKey || extractCacheKey(src)
|
||
return {
|
||
uri: src,
|
||
cacheKey: autoCacheKey,
|
||
}
|
||
} else {
|
||
// 本地文件路径
|
||
return { uri: src }
|
||
}
|
||
}
|
||
}, [src, propSource, cacheKey])
|
||
|
||
const imgProps = {
|
||
style: [style, imageStyle],
|
||
ref,
|
||
// placeholder: blurhash,
|
||
source: imageSource,
|
||
cachePolicy: 'memory-disk' as const,
|
||
errorSource,
|
||
...reset,
|
||
}
|
||
|
||
const handlePress = () => {
|
||
onClick && onClick()
|
||
}
|
||
|
||
if (onClick) {
|
||
return (
|
||
<TouchableOpacity onPress={handlePress} {...touchProps}>
|
||
<ExpoImage
|
||
{...imgProps}
|
||
transition={{ duration: 200, effect: 'cross-dissolve' }}
|
||
// onLoad={(event) => {
|
||
// const { cacheType } = event
|
||
// console.log('缓存类型:', cacheType)
|
||
// // cacheType 可能的值:
|
||
// // - 'none' - 没有使用缓存,从网络加载
|
||
// // - 'disk' - 从磁盘缓存加载
|
||
// // - 'memory' - 从内存缓存加载
|
||
// }}
|
||
/>
|
||
</TouchableOpacity>
|
||
)
|
||
}
|
||
|
||
return <ExpoImage {...imgProps} />
|
||
})
|
||
|
||
Img.displayName = 'Img'
|
||
export default Img
|