Skip to content

Commit 8a7bdaf

Browse files
committed
fix(ImageViewer): 单张图片增加放大缩小功能
1 parent a89bb64 commit 8a7bdaf

File tree

1 file changed

+54
-37
lines changed

1 file changed

+54
-37
lines changed

packages/core/src/ImageViewer/index.tsx

Lines changed: 54 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
import React, { useState, useMemo, useRef } from 'react';
1+
import React, { useState, useMemo, useRef, useEffect } from 'react';
22
import { StyleSheet, ViewProps, Dimensions, View, Image, Animated } from 'react-native';
3-
import TransitionImage, { ImageProps } from '../TransitionImage';
4-
import MaskLayer, { MaskLayerProps } from '../MaskLayer';
3+
import TransitionImage from '../TransitionImage';
4+
import MaskLayer from '../MaskLayer';
55
import Swiper from '../Swiper';
66
import { ActivityIndicator } from 'react-native';
77
export let ImageMainWidth = Dimensions.get('window').width;
88
export let ImageMainHeight = Dimensions.get('window').height;
9+
import {
10+
PinchGestureHandler,
11+
PinchGestureHandlerGestureEvent,
12+
HandlerStateChangeEvent,
13+
PinchGestureHandlerEventPayload,
14+
} from 'react-native-gesture-handler';
915

1016
const defaultImage = 'https://wx3.sinaimg.cn/mw690/4718260ely1gt2cg7t5udj23dw1wkhdu.jpg';
1117

@@ -25,12 +31,31 @@ export interface ImageViewerProps extends ViewProps {
2531
}
2632

2733
function ImageViewer(props: ImageViewerProps) {
28-
const { width = 150, height = 150, src = defaultImage, defaultIndex = 0, ...others } = props;
29-
const [visible, setVisible] = useState(false);
34+
const { width = 150, height = 150, src = defaultImage, defaultIndex = 0 } = props;
3035
const [index, setIndex] = useState<number>(0);
36+
const [visible, setVisible] = useState(false);
37+
const [canVisible, setCanVisible] = useState(true);
3138
const fadeAnim = useRef(new Animated.Value(0)).current;
3239

33-
useMemo(() => {
40+
const scale = useRef(new Animated.Value(1)).current; // 初始缩放比例为 1
41+
const lastScale = useRef(1); // 上一次的缩放比例
42+
43+
const onPinchGestureEvent = (event: PinchGestureHandlerGestureEvent) => {
44+
if (event.nativeEvent.scale) {
45+
// 更新缩放比例
46+
scale.setValue(lastScale.current * event.nativeEvent.scale);
47+
}
48+
};
49+
50+
const onPinchHandlerStateChange = (event: HandlerStateChangeEvent<PinchGestureHandlerEventPayload>) => {
51+
if (event.nativeEvent.oldState === 4 && event.nativeEvent.state === 5) {
52+
// 手势结束后,保存缩放比例
53+
lastScale.current *= event.nativeEvent.scale;
54+
scale.setValue(lastScale.current);
55+
}
56+
};
57+
58+
useEffect(() => {
3459
if (!visible) {
3560
fadeAnim.setValue(0);
3661
return;
@@ -49,45 +74,37 @@ function ImageViewer(props: ImageViewerProps) {
4974
return src;
5075
}, [src]);
5176

52-
const onImgClick = (index: number) => {
53-
setIndex(index);
54-
setVisible(true);
55-
};
77+
const PinchGestureHandlerChild = (url: string) =>
78+
useMemo(
79+
() => (
80+
<PinchGestureHandler onGestureEvent={onPinchGestureEvent} onHandlerStateChange={onPinchHandlerStateChange}>
81+
<Animated.View style={[{ transform: [{ scale }] }]}>
82+
<Image style={{ width: '100%', height: '100%', resizeMode: 'contain' }} source={{ uri: url }} />
83+
</Animated.View>
84+
</PinchGestureHandler>
85+
),
86+
[src, scale],
87+
);
5688

5789
return (
5890
<View style={{}}>
59-
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
60-
{Array.isArray(src) ? (
61-
src.map((item: ImageViewerDataSourceProps, index: number) => {
62-
return (
63-
<TransitionImage
64-
key={index}
65-
style={{ width: width, height: height, flex: 1 }}
66-
onPress={() => onImgClick(index)}
67-
source={{ uri: item.url }}
68-
PlaceholderContent={<ActivityIndicator />}
69-
transition={true}
70-
transitionDuration={500}
71-
/>
72-
);
73-
})
74-
) : (
75-
<TransitionImage
76-
style={{ width: width, height: height }}
77-
onPress={() => setVisible(true)}
78-
source={{ uri: imgUrl }}
79-
PlaceholderContent={<ActivityIndicator />}
80-
transition={true}
81-
transitionDuration={500}
82-
/>
83-
)}
84-
</View>
91+
<TransitionImage
92+
style={{ width: width, height: height }}
93+
onPress={() => canVisible && setVisible(true)}
94+
source={{ uri: imgUrl }}
95+
PlaceholderContent={<ActivityIndicator />}
96+
transition={true}
97+
transitionDuration={500}
98+
onError={(e) => {
99+
if (e?.nativeEvent?.error) setCanVisible(false);
100+
}}
101+
/>
85102
<MaskLayer visible={visible} onDismiss={() => setVisible(false)} opacity={0.9}>
86103
<Animated.View style={[styles.content, { opacity: fadeAnim }]}>
87104
{Array.isArray(src) ? (
88105
<Swiper dataSource={src} height={200} autoplay={false} index={index} />
89106
) : (
90-
<Image style={{ width: '100%', height: '100%', resizeMode: 'contain' }} source={{ uri: src }} />
107+
PinchGestureHandlerChild(imgUrl)
91108
)}
92109
</Animated.View>
93110
</MaskLayer>

0 commit comments

Comments
 (0)