img改为skia
This commit is contained in:
parent
5539a89dcc
commit
47f45bf85a
|
|
@ -1,25 +1,25 @@
|
|||
import { Image as ExpoImage, type ImageProps as ExpoImageProps } from 'expo-image'
|
||||
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 extends ExpoImageProps {
|
||||
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
|
||||
}
|
||||
|
||||
// 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 Img = forwardRef<any, ImgProps>((props, ref) => {
|
||||
const {
|
||||
className = '',
|
||||
style = {},
|
||||
|
|
@ -28,13 +28,25 @@ const Img = forwardRef<ExpoImage, ImgProps>((props, ref) => {
|
|||
source: propSource,
|
||||
cacheKey,
|
||||
width = 256,
|
||||
height,
|
||||
isCompression = false,
|
||||
isWebP = true,
|
||||
...reset
|
||||
...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'
|
||||
|
|
@ -74,22 +86,38 @@ const Img = forwardRef<ExpoImage, ImgProps>((props, ref) => {
|
|||
}
|
||||
}, [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,
|
||||
// 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 <ExpoImage {...imgProps} />
|
||||
|
||||
// return <Image style={[style, imageStyle]} source={{ uri: compressionUrl(src as string) }} />
|
||||
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'
|
||||
|
|
|
|||
|
|
@ -443,7 +443,7 @@ const Index = observer(function Index() {
|
|||
}}
|
||||
/>
|
||||
<FlashList
|
||||
contentContainerStyle={{ paddingBottom: 200, marginTop: 12 }}
|
||||
contentContainerStyle={{ paddingBottom: 200 }}
|
||||
ListFooterComponent={
|
||||
loadingMore ? (
|
||||
<Block className="items-center py-[20px]">
|
||||
|
|
@ -662,8 +662,6 @@ const FilterSection = memo<FilterSectionProps>(function FilterSection({ activeTa
|
|||
<Block className="flex-row gap-[4px]">
|
||||
{tabs.map(({ label, state }) => {
|
||||
const isActive = activeTab === state
|
||||
// 估算宽度:根据文本长度动态计算
|
||||
// 中文字符大约 13px(字体13px),padding 左右各 16px
|
||||
const buttonWidth = Math.max(label.length * 13 + 12, 66) // 最小宽度 70,适应13px字体
|
||||
const buttonHeight = 30 // 稍微增加高度以适应13px字体
|
||||
return (
|
||||
|
|
|
|||
Loading…
Reference in New Issue