1import React from 'react'; 2import { Animated, StyleSheet, View } from 'react-native'; 3import { 4 PanGestureHandler, 5 PanGestureHandlerStateChangeEvent, 6 PinchGestureHandler, 7 PinchGestureHandlerStateChangeEvent, 8 RotationGestureHandler, 9 RotationGestureHandlerStateChangeEvent, 10 State, 11} from 'react-native-gesture-handler'; 12 13const USE_NATIVE_DRIVER = true; 14 15class PinchableBox extends React.Component { 16 panRef = React.createRef<PanGestureHandler>(); 17 rotationRef = React.createRef<RotationGestureHandler>(); 18 pinchRef = React.createRef<PinchGestureHandler>(); 19 20 _baseScale: Animated.Value; 21 _pinchScale: Animated.Value; 22 _scale: Animated.AnimatedMultiplication<number>; 23 _lastScale: number; 24 _onPinchGestureEvent: (...args: any[]) => void; 25 26 _rotate: Animated.Value; 27 _rotateStr: Animated.AnimatedInterpolation<number>; 28 _lastRotate: number; 29 _onRotateGestureEvent?: (...args: any[]) => void; 30 31 _tilt: Animated.Value; 32 _tiltStr: Animated.AnimatedInterpolation<number>; 33 _lastTilt: number; 34 _onTiltGestureEvent: (...args: any[]) => void; 35 36 constructor(props: any) { 37 super(props); 38 39 /* Pinching */ 40 this._baseScale = new Animated.Value(1); 41 this._pinchScale = new Animated.Value(1); 42 this._scale = Animated.multiply(this._baseScale, this._pinchScale); 43 this._lastScale = 1; 44 this._onPinchGestureEvent = Animated.event([{ nativeEvent: { scale: this._pinchScale } }], { 45 useNativeDriver: USE_NATIVE_DRIVER, 46 }); 47 48 /* Rotation */ 49 this._rotate = new Animated.Value(0); 50 this._rotateStr = this._rotate.interpolate({ 51 inputRange: [-100, 100], 52 outputRange: ['-100rad', '100rad'], 53 }); 54 this._lastRotate = 0; 55 this._onRotateGestureEvent = Animated.event([{ nativeEvent: { rotation: this._rotate } }], { 56 useNativeDriver: USE_NATIVE_DRIVER, 57 }); 58 59 /* Tilt */ 60 this._tilt = new Animated.Value(0); 61 this._tiltStr = this._tilt.interpolate({ 62 inputRange: [-501, -500, 0, 1], 63 outputRange: ['1rad', '1rad', '0rad', '0rad'], 64 }); 65 this._lastTilt = 0; 66 this._onTiltGestureEvent = Animated.event([{ nativeEvent: { translationY: this._tilt } }], { 67 useNativeDriver: USE_NATIVE_DRIVER, 68 }); 69 } 70 71 _onRotateHandlerStateChange = (event: RotationGestureHandlerStateChangeEvent) => { 72 if (event.nativeEvent.oldState === State.ACTIVE) { 73 this._lastRotate += event.nativeEvent.rotation; 74 this._rotate.setOffset(this._lastRotate); 75 this._rotate.setValue(0); 76 } 77 }; 78 _onPinchHandlerStateChange = (event: PinchGestureHandlerStateChangeEvent) => { 79 if (event.nativeEvent.oldState === State.ACTIVE) { 80 this._lastScale *= event.nativeEvent.scale; 81 this._baseScale.setValue(this._lastScale); 82 this._pinchScale.setValue(1); 83 } 84 }; 85 _onTiltGestureStateChange = (event: PanGestureHandlerStateChangeEvent) => { 86 if (event.nativeEvent.oldState === State.ACTIVE) { 87 this._lastTilt += event.nativeEvent.translationY; 88 this._tilt.setOffset(this._lastTilt); 89 this._tilt.setValue(0); 90 } 91 }; 92 render() { 93 return ( 94 <PanGestureHandler 95 ref={this.panRef} 96 onGestureEvent={this._onTiltGestureEvent} 97 onHandlerStateChange={this._onTiltGestureStateChange} 98 minDist={10} 99 minPointers={2} 100 maxPointers={2} 101 avgTouches> 102 <Animated.View style={styles.wrapper}> 103 <RotationGestureHandler 104 ref={this.rotationRef} 105 simultaneousHandlers={this.pinchRef} 106 onGestureEvent={this._onRotateGestureEvent} 107 onHandlerStateChange={this._onRotateHandlerStateChange}> 108 <Animated.View style={styles.wrapper}> 109 <PinchGestureHandler 110 ref={this.pinchRef} 111 simultaneousHandlers={this.rotationRef} 112 onGestureEvent={this._onPinchGestureEvent} 113 onHandlerStateChange={this._onPinchHandlerStateChange}> 114 <Animated.View style={styles.container} collapsable={false}> 115 <Animated.Image 116 style={[ 117 styles.pinchableImage, 118 { 119 transform: [ 120 { perspective: 200 }, 121 { scale: this._scale }, 122 { rotate: this._rotateStr }, 123 { rotateX: this._tiltStr }, 124 ], 125 }, 126 ]} 127 source={require('../../assets/images/swmansion.png')} 128 /> 129 </Animated.View> 130 </PinchGestureHandler> 131 </Animated.View> 132 </RotationGestureHandler> 133 </Animated.View> 134 </PanGestureHandler> 135 ); 136 } 137} 138 139const GestureHandlerPinchScreen = () => ( 140 <View style={{ flex: 1 }}> 141 <PinchableBox /> 142 <PinchableBox /> 143 </View> 144); 145 146GestureHandlerPinchScreen.navigationOptions = { 147 title: 'Pinch and Rotate', 148}; 149 150export default GestureHandlerPinchScreen; 151 152const styles = StyleSheet.create({ 153 container: { 154 ...StyleSheet.absoluteFillObject, 155 backgroundColor: 'black', 156 overflow: 'hidden', 157 alignItems: 'center', 158 flex: 1, 159 justifyContent: 'center', 160 }, 161 pinchableImage: { 162 width: 250, 163 height: 250, 164 }, 165 wrapper: { 166 flex: 1, 167 }, 168}); 169