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 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'
|
import tw from 'twrnc'
|
||||||
|
|
||||||
interface ImgProps extends ExpoImageProps {
|
interface ImgProps {
|
||||||
className?: string
|
className?: string
|
||||||
src?: string | number
|
src?: string | number
|
||||||
|
/** 画布宽度(同时也作为压缩宽度) */
|
||||||
width?: number
|
width?: number
|
||||||
|
/** 画布高度,不传则等于 width */
|
||||||
|
height?: number
|
||||||
isWebP?: boolean
|
isWebP?: boolean
|
||||||
errorSource?: string | number
|
errorSource?: string | number
|
||||||
isCompression?: boolean
|
isCompression?: boolean
|
||||||
/** 自定义缓存键,用于需要重定向的 URL */
|
/** 自定义缓存键,用于需要重定向的 URL */
|
||||||
cacheKey?: string
|
cacheKey?: string
|
||||||
|
style?: StyleProp<ViewStyle>
|
||||||
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExpoImage.clearDiskCache()
|
const Img = forwardRef<any, ImgProps>((props, ref) => {
|
||||||
// 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 {
|
const {
|
||||||
className = '',
|
className = '',
|
||||||
style = {},
|
style = {},
|
||||||
|
|
@ -28,13 +28,25 @@ const Img = forwardRef<ExpoImage, ImgProps>((props, ref) => {
|
||||||
source: propSource,
|
source: propSource,
|
||||||
cacheKey,
|
cacheKey,
|
||||||
width = 256,
|
width = 256,
|
||||||
|
height,
|
||||||
isCompression = false,
|
isCompression = false,
|
||||||
isWebP = true,
|
isWebP = true,
|
||||||
...reset
|
...rest
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const imageStyle = tw`${className}`
|
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
|
// 静态图全部压缩jpg
|
||||||
const compressionUrl = useMemo(() => {
|
const compressionUrl = useMemo(() => {
|
||||||
const f = isWebP ? 'webp' : 'jpg'
|
const f = isWebP ? 'webp' : 'jpg'
|
||||||
|
|
@ -74,22 +86,38 @@ const Img = forwardRef<ExpoImage, ImgProps>((props, ref) => {
|
||||||
}
|
}
|
||||||
}, [src, propSource, cacheKey, isCompression, compressionUrl])
|
}, [src, propSource, cacheKey, isCompression, compressionUrl])
|
||||||
|
|
||||||
const imgProps = {
|
// Skia useImage 仅接受 require、URL 或命名资源
|
||||||
style: [style, imageStyle],
|
const skiaSource = useMemo(() => {
|
||||||
ref,
|
if (imageSource == null) return undefined
|
||||||
source: imageSource,
|
if (typeof imageSource === 'number') return imageSource
|
||||||
// 使用 disk 缓存策略,减少内存占用
|
if (typeof imageSource === 'string') return imageSource
|
||||||
cachePolicy: 'disk' as const,
|
if (typeof (imageSource as any).uri === 'string') return (imageSource as any).uri as string
|
||||||
// 添加内存缓存上限,当内存紧张时优先释放
|
return undefined
|
||||||
recyclingKey: typeof src === 'string' ? src : undefined,
|
}, [imageSource])
|
||||||
errorSource,
|
|
||||||
transition: { duration: 200, effect: 'cross-dissolve' as const },
|
const skImage = useImage(skiaSource as any)
|
||||||
...reset,
|
|
||||||
|
if (!skImage) {
|
||||||
|
// 图片还在加载中或加载失败时,暂时不渲染内容
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return <ExpoImage {...imgProps} />
|
return (
|
||||||
|
<Canvas
|
||||||
// return <Image style={[style, imageStyle]} source={{ uri: compressionUrl(src as string) }} />
|
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'
|
Img.displayName = 'Img'
|
||||||
|
|
|
||||||
|
|
@ -443,7 +443,7 @@ const Index = observer(function Index() {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<FlashList
|
<FlashList
|
||||||
contentContainerStyle={{ paddingBottom: 200, marginTop: 12 }}
|
contentContainerStyle={{ paddingBottom: 200 }}
|
||||||
ListFooterComponent={
|
ListFooterComponent={
|
||||||
loadingMore ? (
|
loadingMore ? (
|
||||||
<Block className="items-center py-[20px]">
|
<Block className="items-center py-[20px]">
|
||||||
|
|
@ -662,8 +662,6 @@ const FilterSection = memo<FilterSectionProps>(function FilterSection({ activeTa
|
||||||
<Block className="flex-row gap-[4px]">
|
<Block className="flex-row gap-[4px]">
|
||||||
{tabs.map(({ label, state }) => {
|
{tabs.map(({ label, state }) => {
|
||||||
const isActive = activeTab === state
|
const isActive = activeTab === state
|
||||||
// 估算宽度:根据文本长度动态计算
|
|
||||||
// 中文字符大约 13px(字体13px),padding 左右各 16px
|
|
||||||
const buttonWidth = Math.max(label.length * 13 + 12, 66) // 最小宽度 70,适应13px字体
|
const buttonWidth = Math.max(label.length * 13 + 12, 66) // 最小宽度 70,适应13px字体
|
||||||
const buttonHeight = 30 // 稍微增加高度以适应13px字体
|
const buttonHeight = 30 // 稍微增加高度以适应13px字体
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue