Skip to content

Commit d42e3a4

Browse files
committed
fix(TransitionImage): 添加图片加载中和加载失败占位符
1 parent 9e18327 commit d42e3a4

File tree

5 files changed

+76
-25
lines changed

5 files changed

+76
-25
lines changed

packages/core/src/ActionSheet/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,5 @@ export default Demo
7373
| containerStyle | 容器样式 | `StyleProp<ViewStyle>` | - |
7474
| textStyle | 文本样式 | `StyleProp<ViewStyle>` | - |
7575
| onPress | 点击 ActionSheetItem 触发的事件 | `(event: GestureResponderEvent) => void` | - |
76+
| disabled | 禁止点击 | `boolean` | `false` |
7677

packages/core/src/ActionSheet/item.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface ActionSheetItemProps {
2626
underlayColor?: string;
2727
onPress?: (event: GestureResponderEvent) => void;
2828
children?: React.ReactNode;
29+
disabled?: boolean;
2930
}
3031

3132
export interface ActionSheetItemState {}
@@ -39,6 +40,7 @@ export default function ActionSheetItem(props: ActionSheetItemProps) {
3940
underlayColor = colorScheme === 'light' ? theme.colors.background : '#1A1A22',
4041
containerStyle,
4142
textStyle,
43+
disabled = false,
4244
children,
4345
} = props;
4446

@@ -48,6 +50,7 @@ export default function ActionSheetItem(props: ActionSheetItemProps) {
4850
activeOpacity={activeOpacity}
4951
underlayColor={underlayColor}
5052
onPress={onPress}
53+
disabled={disabled}
5154
>
5255
<View style={StyleSheet.flatten([styles.actionSheetItem, containerStyle])}>
5356
<Text style={StyleSheet.flatten([styles.actionSheetItemText, textStyle])}>{children}</Text>

packages/core/src/TransitionImage/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,5 @@ import { ImageProps } from 'react-native';
7373
| placeholderStyle | 占位符容器的附加样式(可选) | StyleProp<ViewStyle> | - |
7474
| transition | 在图像加载时执行淡入淡出过渡 | boolean | - |
7575
| transitionDuration | 图像加载时执行淡入淡出过渡时间 | boolean | - |
76+
| disabled | 禁止点击 | boolean | false |
7677

packages/core/src/TransitionImage/index.tsx

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,46 @@ import {
99
ViewStyle,
1010
StyleProp,
1111
ImageStyle,
12+
NativeSyntheticEvent,
13+
ImageLoadEventData,
14+
ImageErrorEventData,
15+
ActivityIndicator,
16+
Text,
1217
} from 'react-native';
18+
import Icon from '../Icon';
1319

1420
export type ImageProps = RNImageProps & {
1521
Component?: typeof React.Component;
1622
onPress?(): void;
1723
onLongPress?(): void;
18-
ImageComponent?: React.ComponentType<any>;
24+
ImageComponent?: React.ComponentType<ImageProps>;
1925
PlaceholderContent?: React.ReactElement<any>;
2026
containerStyle?: StyleProp<ViewStyle>;
2127
childrenContainerStyle?: StyleProp<ViewStyle>;
2228
placeholderStyle?: StyleProp<ViewStyle>;
2329
transition?: boolean;
2430
transitionDuration?: number;
31+
disabled?: boolean;
2532
children?: React.ReactNode;
2633
};
2734

2835
type ImageState = {
2936
placeholderOpacity: Animated.Value;
37+
isLoading: boolean;
38+
hasError: boolean;
3039
};
3140

3241
export default function TransitionImage(props: ImageProps & Partial<ImageProps>) {
33-
const [state, _] = useState<ImageState>({
42+
const [state, setState] = useState<ImageState>({
3443
placeholderOpacity: new Animated.Value(1),
44+
isLoading: true,
45+
hasError: false,
3546
});
3647

37-
const onLoad = (e: any) => {
48+
const onLoad = (e: NativeSyntheticEvent<ImageLoadEventData>) => {
3849
const { transition, onLoad, transitionDuration } = props;
3950
if (!transition) {
51+
setState({ ...state, isLoading: false });
4052
state.placeholderOpacity.setValue(0);
4153
return;
4254
}
@@ -46,9 +58,14 @@ export default function TransitionImage(props: ImageProps & Partial<ImageProps>)
4658
duration: transitionDuration,
4759
useNativeDriver: true,
4860
}).start();
61+
setState({ ...state, isLoading: false });
4962
onLoad && onLoad(e);
5063
};
5164

65+
const onError = (e: NativeSyntheticEvent<ImageErrorEventData>) => {
66+
setState({ ...state, isLoading: false, hasError: true });
67+
};
68+
5269
const {
5370
onPress,
5471
onLongPress,
@@ -60,25 +77,42 @@ export default function TransitionImage(props: ImageProps & Partial<ImageProps>)
6077
style = {},
6178
ImageComponent = ImageNative,
6279
children,
80+
disabled = false,
6381
...attributes
6482
} = props;
6583

6684
const hasImage = Boolean(attributes.source);
6785
const { width, height, ...styleProps } = StyleSheet.flatten(style);
6886

87+
const PlaceholderContentLoading = (
88+
<View style={styles.errorContainer}>
89+
<ActivityIndicator />
90+
<Text style={{ color: '#fff', marginTop: 8 }}>加载中...</Text>
91+
</View>
92+
);
93+
94+
const PlaceholderContentError = (
95+
<View style={styles.errorContainer}>
96+
<Icon name="circle-close-o" color="#fff" size={24} />
97+
<Text style={{ color: '#fff', marginTop: 8 }}>加载失败</Text>
98+
</View>
99+
);
100+
69101
return (
70102
<Component
71103
onPress={onPress}
72104
onLongPress={onLongPress}
73105
accessibilityIgnoresInvertColors={true}
74106
style={StyleSheet.flatten([styles.container, containerStyle])}
107+
disabled={disabled || state.isLoading || state.hasError}
75108
>
76109
<ImageComponent
77110
testID="RNE__Image"
78111
transition={true}
79112
transitionDuration={360}
80113
{...attributes}
81114
onLoad={onLoad}
115+
onError={onError}
82116
style={StyleSheet.flatten([
83117
StyleSheet.absoluteFill,
84118
{
@@ -104,7 +138,9 @@ export default function TransitionImage(props: ImageProps & Partial<ImageProps>)
104138
testID="RNE__Image__placeholder"
105139
style={StyleSheet.flatten([style, styles.placeholder, placeholderStyle])}
106140
>
107-
{PlaceholderContent}
141+
{state.isLoading && PlaceholderContentLoading}
142+
{state.hasError && PlaceholderContentError}
143+
{!state.isLoading && !state.hasError && PlaceholderContent}
108144
</View>
109145
</Animated.View>
110146

@@ -137,4 +173,13 @@ const styles = StyleSheet.create({
137173
alignItems: 'center',
138174
justifyContent: 'center',
139175
},
176+
errorContainer: {
177+
display: 'flex',
178+
flexDirection: 'column',
179+
alignItems: 'center',
180+
justifyContent: 'center',
181+
backgroundColor: 'rgba(50, 50, 51, .88)',
182+
height: '100%',
183+
width: '100%',
184+
},
140185
});

packages/react-native-image-picker/src/ImagePicker/index.tsx

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
ImageRequireSource,
1010
} from 'react-native';
1111
import { CameraOptions, ImagePickerResponse } from 'react-native-image-picker';
12-
import { Theme, Flex, ActionSheet, ActionSheetItem, TransitionImage, Icon, Text } from '@uiw/react-native';
12+
import { Theme, Flex, ActionSheet, ActionSheetItem, TransitionImage, Icon } from '@uiw/react-native';
1313
import ImageView from 'react-native-image-viewing';
1414
import { useTheme } from '@shopify/restyle';
1515
import useImagePicker from './useImagePicker';
@@ -149,15 +149,13 @@ const ImagePicker = ({
149149
<TransitionImage
150150
source={{ uri: item }}
151151
style={{ width, height, borderRadius: 2 }}
152-
PlaceholderContent={<Text color="text">加载失败</Text>}
153152
transition={true}
153+
disabled={loading}
154+
onPress={() => previewImage(index)}
154155
/>
155-
<View style={styles.previewBox}>
156-
<TouchableOpacity disabled={loading} onPress={() => previewImage(index)} style={styles.previewIcon}>
157-
<Icon name="eye" color={theme.colors.primary_background || '#3578e5'} size={16} />
158-
</TouchableOpacity>
159-
<TouchableOpacity disabled={loading} onPress={() => deleteImage(index)} style={styles.previewIcon}>
160-
<Icon name="delete" color={theme.colors.primary_background || '#3578e5'} size={16} />
156+
<View style={styles.deleteBox}>
157+
<TouchableOpacity disabled={loading} onPress={() => deleteImage(index)}>
158+
<Icon name="close" color="#fff" size={10} />
161159
</TouchableOpacity>
162160
</View>
163161
</View>
@@ -184,14 +182,18 @@ const ImagePicker = ({
184182
disabled={loading}
185183
style={{ justifyContent: 'center', alignItems: 'center', width, height }}
186184
>
187-
<Icon name="plus-square-o" color={theme.colors.text || '#A9A9A9'} size={20} />
185+
<Icon name="camera-o" color={theme.colors.gray300 || '#ccc'} size={24} />
188186
</TouchableOpacity>
189187
</View>
190188
</Flex.Item>
191189
</Flex>
192190
<ActionSheet onCancel={setLaunchVisibleFalse} visible={launchVisible}>
193-
<ActionSheetItem onPress={launchLibrary}>{launchLibraryText}</ActionSheetItem>
194-
<ActionSheetItem onPress={launchCamera}>{launchCameraText}</ActionSheetItem>
191+
<ActionSheetItem disabled={loading} onPress={launchLibrary}>
192+
{launchLibraryText}
193+
</ActionSheetItem>
194+
<ActionSheetItem disabled={loading} onPress={launchCamera}>
195+
{launchCameraText}
196+
</ActionSheetItem>
195197
</ActionSheet>
196198
<ImageView
197199
visible={previewVisible}
@@ -218,21 +220,20 @@ function createStyles({ width = 100, height = 100, borderColor = '#CCCCCC', back
218220
backgroundColor: backgroundColor,
219221
margin: 4,
220222
},
221-
previewBox: {
223+
deleteBox: {
222224
position: 'absolute',
223-
left: 0,
225+
right: 0,
224226
top: 0,
225227
flexDirection: 'row',
226228
alignItems: 'center',
227229
justifyContent: 'center',
228-
height: height,
229-
width: width,
230-
},
231-
previewIcon: {
232-
alignItems: 'center',
233-
justifyContent: 'center',
234-
width: width * 0.375,
235-
height: height * 0.375,
230+
height: 18,
231+
width: 18,
232+
backgroundColor: 'rgba(0, 0, 0, .7)',
233+
borderTopLeftRadius: 0,
234+
borderTopRightRadius: 0,
235+
borderBottomRightRadius: 0,
236+
borderBottomLeftRadius: 12,
236237
},
237238
});
238239
}

0 commit comments

Comments
 (0)