openspacelabs / react-native-zoomable-view

A view component for react-native with pinch to zoom, tap to move and double tap to zoom capability.
MIT License
205 stars 57 forks source link

Add translateX and translateY info in an optional callback #61

Open Andre-the-Viking opened 1 year ago

Andre-the-Viking commented 1 year ago

First, thanks for the library and the support, it really works as expected (pinch zooming, panning, etc.)

In our project, we want to be able to pinch zoom a View having also a potential canvas role so the user is able to add drawing elements with gesture. Here the demo of zoom mode and draw mode in action:

https://user-images.githubusercontent.com/126488814/224100901-67fe0271-7bf3-45c1-995a-2a3d72b248e8.mov

For being able to do this, we have to strike the pose when the user presses the drawing tool. For this we need the final result of translateX and translateY :

  render() {
    return (
      <View
        style={styles.container}
        {...this.gestureHandlers.panHandlers}
        ref={this.zoomSubjectWrapperRef}
        onLayout={this.grabZoomSubjectOriginalMeasurements}
      >
        <Animated.View
          style={[
            styles.zoomSubject,
            this.props.style,
            {
              transform: [
                { scale: this.zoomAnim },
                ...this.panAnim.getTranslateTransform(), // <=== need the final result, once animations are completed
              ],
            },
          ]}
        >
          {this.props.children}

so once animations are completed (we have detected 3 places in the ReactNativeZoomableView.tsx where animations .start(...)) we patched the lib in order to call an optional onTranslateXY method call like so:

            const event = this._getZoomableViewEventObject({
              translateX: this.panAnim.x._value,
              translateY: this.panAnim.y._value,
            });
            this.props.onTranslateXY?.(event);

And then the strike the pose magic can happen for drawing mode like you can see in the shared demo.

Let us know if that makes sense

leaf541 commented 1 year ago

Hi, could you please explain a little more what you did as I'm facing the same problems and it seems your solution would help me a lot.

Thank you.

Andre-the-Viking commented 1 year ago

Here the patch we made for @openspacelabs+react-native-zoomable-view+2.1.1.patch:

diff --git a/node_modules/@openspacelabs/react-native-zoomable-view/src/ReactNativeZoomableView.tsx b/node_modules/@openspacelabs/react-native-zoomable-view/src/ReactNativeZoomableView.tsx
index 271813c..7aa33a4 100644
--- a/node_modules/@openspacelabs/react-native-zoomable-view/src/ReactNativeZoomableView.tsx
+++ b/node_modules/@openspacelabs/react-native-zoomable-view/src/ReactNativeZoomableView.tsx
@@ -207,6 +207,11 @@ class ReactNativeZoomableView extends Component<
           offsetState.boundaryCrossedAnimInEffect = true;
           getBoundaryCrossedAnim(this.panAnim[axis], boundOffset).start(() => {
             offsetState.boundaryCrossedAnimInEffect = false;
+            const event = this._getZoomableViewEventObject({
+              translateX: this.panAnim.x._value,
+              translateY: this.panAnim.y._value,
+            });
+            this.props.onTranslateXY?.(event);
           });
           return;
         }
@@ -423,7 +428,13 @@ class ReactNativeZoomableView extends Component<
       getPanMomentumDecayAnim(this.panAnim, {
         x: gestureState.vx / this.zoomLevel,
         y: gestureState.vy / this.zoomLevel,
-      }).start();
+      }).start(() => {
+        const event = this._getZoomableViewEventObject({
+          translateX: this.panAnim.x._value,
+          translateY: this.panAnim.y._value,
+        });
+        this.props.onTranslateXY?.(event);
+      });
     }

     if (this.longPressTimeout) {
@@ -920,8 +931,15 @@ class ReactNativeZoomableView extends Component<
       });
       prevScale = newScale;
     });
+
     getZoomToAnimation(this.zoomAnim, newZoomLevel).start(() => {
       this.zoomAnim.removeListener(listenerId);
+      const event = this._getZoomableViewEventObject({
+        zoomLevel: newZoomLevel,
+        translateX: this.panAnim.x._value,
+        translateY: this.panAnim.y._value,
+      });
+      this.props.onTranslateXY?.(event);
     });
     // == Zoom Animation Ends ==
leaf541 commented 1 year ago

Thank you, so you added this and created method onTranslateXY? Also if you could help me, I'm trying to find out the coordinates of the zoomed in view so I can make drag and drop feature. Drop should happen on the Zoomable View but I'm struggling to find out how to calculate the correct data so I can position these elements absolutely inside it.

Andre-the-Viking commented 1 year ago

For the zoom mode:

Then for the strike the pose mode:

    <View style={{ transform: [{ scale: zoomLevel }, { translateX }, { translateY }] }}>
      ...
    </View>