expo-duooomi-app/@share/components/Img.tsx

125 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<ViewStyle>
[key: string]: any
}
const Img = forwardRef<any, ImgProps>((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 (
<Canvas
ref={ref}
style={[style, imageStyle, { width: canvasWidth, height: canvasHeight }]}
{...rest}
>
<SkiaImage
image={skImage}
x={0}
y={0}
width={canvasWidth}
height={canvasHeight}
fit="contain"
/>
</Canvas>
)
})
Img.displayName = 'Img'
export default memo(Img)