Skip to content

Commit a56864a

Browse files
committed
Update demo.
1 parent 7d554c1 commit a56864a

File tree

3 files changed

+105
-33
lines changed

3 files changed

+105
-33
lines changed

demo/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ const App: () => Node = () => {
8383
visible={visible}
8484
onClose={() => setVisible(false)}
8585
animationType="slide"
86-
debug={false}
86+
debug
8787
/>
8888
</SafeAreaView>
8989
);
Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,27 @@
11
import React from 'react';
2-
import {TouchableOpacity, View} from 'react-native';
2+
import {TouchableWithoutFeedback} from 'react-native';
33

44
export default function DoubleTap(props: {children: any}) {
5-
const onPress = () => console.log('foo.', Date.now());
5+
let lastPressed = 0;
6+
const onPress = React.useCallback(() => {
7+
console.log('onPress', lastPressed);
8+
if (lastPressed === 0) {
9+
lastPressed = Date.now();
10+
console.log(' -> ', lastPressed);
11+
} else {
12+
const diff = Date.now() - lastPressed;
13+
console.log(diff);
14+
if (diff <= 500) {
15+
console.log('double tap!');
16+
}
17+
18+
lastPressed = 0;
19+
}
20+
}, []);
621

722
return (
8-
<TouchableOpacity disabled={false} onPress={() => onPress()} activeOpacity={0.4} {...props}>
9-
<View style={{flex:1, backgroundColor: 'blue'}}>{props.children}</View>
10-
</TouchableOpacity>
23+
<TouchableWithoutFeedback onPress={onPress}>
24+
{props.children}
25+
</TouchableWithoutFeedback>
1126
);
1227
}

demo/components/ImageViewer/Image.tsx

Lines changed: 84 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@ import {
1313
Text,
1414
SafeAreaView,
1515
Platform,
16-
TouchableOpacity,
1716
} from 'react-native';
1817
import {ImageComponentOptionalProps, ImageComponentProps, ImageComponentState} from './types';
19-
import DoubleTap from './DoubleTap';
2018

2119
const SCREEN_WIDTH = Dimensions.get('window').width;
2220
const SCREEN_HEIGHT = Dimensions.get('window').height;
@@ -39,6 +37,11 @@ class ImageComponent extends React.Component<ImageComponentProps, ImageComponent
3937
private _lastScale: number = 1;
4038

4139
private _isGestureMoved: boolean = false;
40+
private _lastTap: number = 0;
41+
42+
private _distance: number = 150.0;
43+
private _isPinching: boolean = false;
44+
private _currentScale: number = 1.0;
4245

4346
constructor(props: ImageComponentProps) {
4447
super(props);
@@ -53,29 +56,53 @@ class ImageComponent extends React.Component<ImageComponentProps, ImageComponent
5356
this._translateXY = new Animated.ValueXY();
5457
this._scale = new Animated.Value(1);
5558

56-
const onShouldSetPanResponder = this._onShouldSetPanResponder.bind(this);
57-
const onPanMove = this._onPanResponderMove.bind(this);
58-
const onPanEnd = this._onPanResponderEnd.bind(this);
59-
6059
this._panResponder = PanResponder.create({
61-
onStartShouldSetPanResponder: onShouldSetPanResponder,
62-
onStartShouldSetPanResponderCapture: () => true,
60+
onStartShouldSetPanResponder: this._onShouldSetPanResponder,
61+
// onStartShouldSetPanResponderCapture: () => true,
6362

6463
onMoveShouldSetPanResponder: () => true,
6564
onMoveShouldSetPanResponderCapture: () => true,
6665

67-
onPanResponderTerminate: onPanEnd,
68-
onPanResponderRelease: onPanEnd,
69-
onPanResponderMove: onPanMove,
66+
// onPanResponderTerminate: onPanEnd,
67+
onPanResponderTerminationRequest: () => false,
68+
69+
onPanResponderRelease: this._onPanResponderRelease,
70+
onPanResponderMove: this._onPanResponderMove,
71+
72+
onPanResponderGrant: this._onPanResponderGrant,
73+
onShouldBlockNativeResponder: () => true,
7074
});
7175
}
7276

73-
private _onShouldSetPanResponder(evt: GestureResponderEvent) {
74-
return evt.nativeEvent.touches.length === 1;
77+
private _onShouldSetPanResponder = (evt: GestureResponderEvent) => evt.nativeEvent.touches.length <= 2;
78+
79+
private _onPanResponderGrant = (evt: GestureResponderEvent, gesture: PanResponderGestureState) => {
80+
if (gesture.numberActiveTouches === 2) {
81+
const dx = Math.abs(evt.nativeEvent.touches[0].pageX - evt.nativeEvent.touches[1].pageX)
82+
const dy = Math.abs(evt.nativeEvent.touches[0].pageY - evt.nativeEvent.touches[1].pageY);
83+
84+
this._distance = Math.sqrt(dx * dx + dy * dy);
85+
this._debug('_onPanResponderGrant', 'distance', this._distance);
86+
this._isPinching = true;
87+
}
7588
}
7689

77-
private _onPanResponderMove(_evt: any, gesture: PanResponderGestureState) {
90+
private _onPanResponderMove = (evt: GestureResponderEvent, gesture: PanResponderGestureState) => {
7891
this._debug('_onPanResponderMove', 'dx', gesture.dx, 'dy', gesture.dy, this._lastOffset);
92+
if (gesture.numberActiveTouches === 2) {
93+
const dx = Math.abs(evt.nativeEvent.touches[0].pageX - evt.nativeEvent.touches[1].pageX)
94+
const dy = Math.abs(evt.nativeEvent.touches[0].pageY - evt.nativeEvent.touches[1].pageY);
95+
96+
const distance = Math.sqrt(dx * dx + dy * dy);
97+
const scale = (distance / this._distance) * this._lastScale;
98+
this._debug('_onPanResponderMove', 'pinch to zoom', 'scale', scale);
99+
this._onPinchUpdate({scale});
100+
101+
this._currentScale = scale;
102+
103+
return;
104+
}
105+
79106
this._isGestureMoved = true;
80107

81108
if (this._lastScale > this._getMinimumScale()) {
@@ -107,7 +134,26 @@ class ImageComponent extends React.Component<ImageComponentProps, ImageComponent
107134
this.props.onZoomStateChange(true);
108135
this._translateXY.setValue({x: 0, y: Math.max(0, gesture.dy)});
109136
}
110-
private _onPanResponderEnd(_evt: any, gesture: PanResponderGestureState) {
137+
private _onPanResponderRelease = (_evt: GestureResponderEvent, gesture: PanResponderGestureState) => {
138+
this._debug('_onPanResponderRelease', 'gesture.numberActiveTouches', gesture.numberActiveTouches);
139+
if (this._isPinching) {
140+
this._isPinching = false;
141+
this._onPinchEnd({scale: this._currentScale});
142+
return;
143+
}
144+
145+
if (gesture.numberActiveTouches === 0) {
146+
this._debug('_onPanResponderRelease', 'gesture.dx', gesture.dx, 'gesture.dy', gesture.dy);
147+
if (Math.abs(gesture.dx) < 10 && Math.abs(gesture.dy) < 10) {
148+
this._handleTap({
149+
x: gesture.x0,
150+
y: gesture.y0
151+
});
152+
153+
return;
154+
}
155+
}
156+
111157
if (!this._isGestureMoved) {
112158
return;
113159
}
@@ -152,7 +198,7 @@ class ImageComponent extends React.Component<ImageComponentProps, ImageComponent
152198
private _getMaximumScale = (): number => 2.5;
153199
private _getMinimumScale = (): number => 1.0;
154200

155-
private _handleImageZoomInOut = (evt: GestureStateChangeEvent<any>) => {
201+
private _handleImageZoomInOut = (evt: {x: number; y: number}) => {
156202
this._debug('double tab triggered', '_scaleNum', this._lastScale, evt, 'ratio', this._getRatio());
157203

158204
if (this._lastScale > this._getMinimumScale()) {
@@ -230,6 +276,19 @@ class ImageComponent extends React.Component<ImageComponentProps, ImageComponent
230276
}
231277
};
232278

279+
private _handleTap = (tapCoords: {x: number; y: number}) => {
280+
const now = (new Date()).getTime();
281+
const delta = now - this._lastTap;
282+
283+
this._debug('_handleTap', '_lastTap', this._lastTap, 'delta', delta);
284+
if (delta <= 500) {
285+
// double tap
286+
this._handleImageZoomInOut(tapCoords);
287+
}
288+
289+
this._lastTap = now;
290+
};
291+
233292
private _getRatio = () =>
234293
this.state.width
235294
? this.state.width >= SCREEN_WIDTH
@@ -252,7 +311,7 @@ class ImageComponent extends React.Component<ImageComponentProps, ImageComponent
252311

253312
private _onImageLoadEnd = () => this.setState({loading: false});
254313

255-
private _onPinchEnd = (evt: GestureStateChangeEvent<any>) => {
314+
private _onPinchEnd = (evt: {scale: number}) => {
256315
this._debug('_onPinchEnd', evt);
257316

258317
let scale = evt.scale as number;
@@ -282,8 +341,8 @@ class ImageComponent extends React.Component<ImageComponentProps, ImageComponent
282341
}),
283342
]).start();
284343
};
285-
private _onPinchUpdate = (evt: GestureStateChangeEvent<any>) => {
286-
const scale = this._lastScale * (evt.scale as number);
344+
private _onPinchUpdate = (evt: {scale: number}) => {
345+
const scale = evt.scale;
287346

288347
if (scale < this._getMinimumScale()) {
289348
this._scale.setValue(this._getMinimumScale());
@@ -429,14 +488,12 @@ class ImageComponent extends React.Component<ImageComponentProps, ImageComponent
429488
return (
430489
<View style={styles.container}>
431490
<Animated.View style={backdropStyle} {...this._panResponder.panHandlers} />
432-
<DoubleTap style={{flex: 1}}>
433-
<Animated.View
434-
style={moveObjStyle}
435-
{...(this.state.isZooming || Platform.OS === 'ios' ? this._panResponder.panHandlers : {})}
436-
renderToHardwareTextureAndroid>
437-
<RNImage source={this.props.source} style={computeImageStyle} onLoadEnd={this._onImageLoadEnd} />
438-
</Animated.View>
439-
</DoubleTap>
491+
<Animated.View
492+
style={moveObjStyle}
493+
{...(this.state.isZooming || Platform.OS === 'ios' ? this._panResponder.panHandlers : {})}
494+
renderToHardwareTextureAndroid>
495+
<RNImage source={this.props.source} style={computeImageStyle} onLoadEnd={this._onImageLoadEnd} />
496+
</Animated.View>
440497
<SafeAreaView style={styles.safeAreaContainer} pointerEvents="none">
441498
{this._renderHeader()}
442499
{this.state.loading && <ActivityIndicator color={'#fff'} />}

0 commit comments

Comments
 (0)