97 lines
2.7 KiB
TypeScript
97 lines
2.7 KiB
TypeScript
import { Image as ExpoImage, type ImageProps as ExpoImageProps } from 'expo-image'
|
||
import React, { forwardRef, memo, useMemo } from 'react'
|
||
import tw from 'twrnc'
|
||
|
||
interface ImgProps extends ExpoImageProps {
|
||
className?: string
|
||
src?: string | number
|
||
width?: number
|
||
isWebP?: boolean
|
||
errorSource?: string | number
|
||
isCompression?: boolean
|
||
/** 自定义缓存键,用于需要重定向的 URL */
|
||
cacheKey?: string
|
||
}
|
||
|
||
// ExpoImage.clearDiskCache()
|
||
// ExpoImage.clearMemoryCache()
|
||
|
||
// cloudflare 图片优化服务示例地址
|
||
const ImageOrigin = `https://bowong.cc/cdn-cgi/image/width=128,quality=75,format=webp/https://cdn.roasmax.cn/material/b59b75841c484d8bafec9c5636930b69.webp`
|
||
|
||
const Img = forwardRef<ExpoImage, ImgProps>((props, ref) => {
|
||
const {
|
||
className = '',
|
||
style = {},
|
||
src,
|
||
errorSource,
|
||
source: propSource,
|
||
cacheKey,
|
||
width = 256,
|
||
isCompression = false,
|
||
isWebP = true,
|
||
...reset
|
||
} = props
|
||
|
||
const imageStyle = tw`${className}`
|
||
|
||
// 静态图全部压缩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])
|
||
|
||
const imgProps = {
|
||
style: [style, imageStyle],
|
||
ref,
|
||
source: imageSource,
|
||
// 使用 disk 缓存策略,减少内存占用
|
||
cachePolicy: 'disk' as const,
|
||
// 添加内存缓存上限,当内存紧张时优先释放
|
||
recyclingKey: typeof src === 'string' ? src : undefined,
|
||
errorSource,
|
||
transition: { duration: 200, effect: 'cross-dissolve' as const },
|
||
...reset,
|
||
}
|
||
|
||
return <ExpoImage {...imgProps} />
|
||
|
||
// return <Image style={[style, imageStyle]} source={{ uri: compressionUrl(src as string) }} />
|
||
})
|
||
|
||
Img.displayName = 'Img'
|
||
export default memo(Img)
|