diff --git a/@share/components/Img.tsx b/@share/components/Img.tsx index 4dcbd42..31250ee 100644 --- a/@share/components/Img.tsx +++ b/@share/components/Img.tsx @@ -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 + [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((props, ref) => { +const Img = forwardRef((props, ref) => { const { className = '', style = {}, @@ -28,13 +28,25 @@ const Img = forwardRef((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((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 - - // return + return ( + + + + ) }) Img.displayName = 'Img' diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index c089d23..f351d69 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -443,7 +443,7 @@ const Index = observer(function Index() { }} /> @@ -662,8 +662,6 @@ const FilterSection = memo(function FilterSection({ activeTa {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 (