@@ -13,10 +13,8 @@ import {
1313 Text ,
1414 SafeAreaView ,
1515 Platform ,
16- TouchableOpacity ,
1716} from 'react-native' ;
1817import { ImageComponentOptionalProps , ImageComponentProps , ImageComponentState } from './types' ;
19- import DoubleTap from './DoubleTap' ;
2018
2119const SCREEN_WIDTH = Dimensions . get ( 'window' ) . width ;
2220const 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