Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | 5x 5x 5x 5x 1x 4x 1x 3x 1x 1x 1x | /**
* PostImage - 论坛帖子图片组件
*
* 功能:
* - 加载状态占位符
* - 错误处理和后备图片
* - 图片加载优化
* - 平滑过渡动画
*/
import React, { useState } from 'react';
import { ActivityIndicator, StyleSheet } from 'react-native';
import { Image } from 'expo-image';
import { Stack, Text } from 'tamagui';
import { ImageOff } from '@tamagui/lucide-icons';
interface PostImageProps {
uri: string;
width: number | string;
height: number | string;
resizeMode?: 'cover' | 'contain';
}
export function PostImage({ uri, width, height, resizeMode = 'cover' }: PostImageProps) {
const [isLoading, setIsLoading] = useState(true);
const [hasError, setHasError] = useState(false);
// 检查URL是否有效
const isValidUrl = uri && uri.trim().length > 0 && !uri.includes('placekitten');
// 如果URL无效,直接显示占位符
if (!isValidUrl) {
return (
<Stack
width={typeof width === 'string' ? undefined : width}
height={typeof height === 'string' ? undefined : height}
flex={typeof width === 'string' ? 1 : undefined}
backgroundColor="#f5f5f5"
alignItems="center"
justifyContent="center"
>
<ImageOff size={32} color="#9ca3af" />
<Text fontSize={12} color="#9ca3af" marginTop="$2">
暂无图片
</Text>
</Stack>
);
}
// 如果加载失败,显示错误占位符
if (hasError) {
return (
<Stack
width={typeof width === 'string' ? undefined : width}
height={typeof height === 'string' ? undefined : height}
flex={typeof width === 'string' ? 1 : undefined}
backgroundColor="#f5f5f5"
alignItems="center"
justifyContent="center"
>
<ImageOff size={32} color="#9ca3af" />
<Text fontSize={12} color="#9ca3af" marginTop="$2">
图片加载失败
</Text>
</Stack>
);
}
return (
<Stack
width={typeof width === 'string' ? undefined : width}
height={typeof height === 'string' ? undefined : height}
flex={typeof width === 'string' ? 1 : undefined}
position="relative"
>
{/* 加载占位符 */}
{isLoading && (
<Stack
position="absolute"
top={0}
left={0}
right={0}
bottom={0}
backgroundColor="#f5f5f5"
alignItems="center"
justifyContent="center"
zIndex={1}
>
<ActivityIndicator size="small" color="#9ca3af" />
</Stack>
)}
{/* 图片 */}
<Image
source={{ uri }}
style={{
width: typeof width === 'number' ? width : '100%',
height: typeof height === 'number' ? height : '100%',
}}
contentFit={resizeMode}
cachePolicy="memory-disk"
transition={300}
onLoadStart={() => setIsLoading(true)}
onLoad={() => setIsLoading(false)}
onError={() => {
setIsLoading(false);
setHasError(true);
}}
priority="normal"
/>
</Stack>
);
}
|