Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't disable PanGestureHandler #255

Closed
farkob opened this issue Jul 16, 2018 · 7 comments
Closed

Can't disable PanGestureHandler #255

farkob opened this issue Jul 16, 2018 · 7 comments

Comments

@farkob
Copy link

farkob commented Jul 16, 2018

I disable a PanGestureHandler through its enabled prop while it's responding, I disable it using setState when a value hits a limit while the interaction is going on. Handler stops responding as expected. However, after that when I start interacting again, even though enabled={false} it keeps responding. (On iOS, haven't tested on Android)

(Also not related but a question: This PanGestureHandler is inside a ScrollView, after I disable the PanGestureHandler, I want ScrollView to respond as normal in that same continuous finger move, how can I do this? Any ideas?)

@osdnk
Copy link
Contributor

osdnk commented Jul 16, 2018

Hi,
I did wish to repro it so I've added enabled={false} (and handled it with state then) to draggable example and it works as I expected. Would you mind providing some code?

@farkob
Copy link
Author

farkob commented Jul 16, 2018

Sure, basically what I do is, to disable it when the image that is panned hits the bounds. It happens on onPan method. You can zoom in on the image by pinching first if you're going to try this and then move it left or right. If I set this.state.disabled = false in the constructor it gets disabled as expected.

import React, { Component } from 'react';
import PropTypes from 'prop-types';

import { View, Image, Animated, Dimensions } from 'react-native';
import { PinchGestureHandler, PanGestureHandler, State } from 'react-native-gesture-handler';

const { width, height } = Dimensions.get('window');

export default class PhotoView extends Component {
  constructor(props) {
    super(props);
    this.state = { disabled: false };
    this.baseScale = new Animated.Value(1);
    this.pinchScale = new Animated.Value(1);
    this.translationX = new Animated.Value(0);
    this.translationY = new Animated.Value(0);
    this.baseTranslationX = new Animated.Value(0);
    this.baseTranslationY = new Animated.Value(0);
    this.translateX = Animated.add(this.translationX, this.baseTranslationX);
    this.translateY = Animated.add(this.translationY, this.baseTranslationY);
    this.scale = Animated.multiply(this.baseScale, this.pinchScale);
    this.windowWidth = new Animated.Value(width);
    this.windowHeight = new Animated.Value(height);

    this.lastScale = 1;
    this.lastTranslationX = 0;
    this.lastTranslationY = 0;
    this.onPanGestureEvent = Animated.event(
      [{ nativeEvent: { translationX: this.translationX, translationY: this.translationY } }],
      {
        useNativeDriver: true,
        listener: this.onPan.bind(this),
      },
    );
    this.onPinchGestureEvent = Animated.event([{ nativeEvent: { scale: this.pinchScale } }], {
      useNativeDriver: true,
    });
    this.onPinchHandlerStateChange = this.onPinchHandlerStateChange.bind(this);
    this.onPanHandlerStateChange = this.onPanHandlerStateChange.bind(this);
    this.interScale = this.scale.interpolate({
      inputRange: [0.3, 3, 8],
      outputRange: [0.3, 3, 4],
      extrapolateLeft: 'clamp',
      extrapolateRight: 'extend',
    });
    this.interTranslateX = Animated.divide(this.translateX, this.interScale);
    this.interTranslateY = Animated.divide(this.translateY, this.interScale);
  }

  onPan({ nativeEvent: { translationX } }) {
    const tX = Math.abs(translationX + this.lastTranslationX);
    const diff = ((width * (1 - this.lastScale)) / 2) + tX;
    if (diff > 0) {
      this.setState({ disabled: true });
    }
  }

  onPinchHandlerStateChange(event) {
    if (event.nativeEvent.oldState === State.ACTIVE) {
      this.lastScale *= event.nativeEvent.scale;
      this.baseScale.setValue(this.lastScale);
      this.pinchScale.setValue(1);
      if (this.lastScale < 1) {
        Animated.spring(this.baseScale, {
          toValue: 1,
          useNativeDriver: true,
        }).start();
        Animated.spring(this.baseTranslationX, {
          toValue: 0,
          useNativeDriver: true,
        }).start();
        Animated.spring(this.baseTranslationY, {
          toValue: 0,
          useNativeDriver: true,
        }).start();
        this.lastScale = 1;
      } else if (this.lastScale > 3) {
        Animated.spring(this.baseScale, {
          toValue: 3,
          useNativeDriver: true,
        }).start();
        this.lastScale = 3;
      }
    }
  }

  onPanHandlerStateChange(event) {
    if (event.nativeEvent.oldState === State.ACTIVE) {
      this.lastTranslationX += event.nativeEvent.translationX;
      this.lastTranslationY += event.nativeEvent.translationY;
      this.baseTranslationX.setValue(this.lastTranslationX);
      this.baseTranslationY.setValue(this.lastTranslationY);
      this.translationX.setValue(0);
      this.translationY.setValue(0);
    }
  }

  render() {
    const { imageSource } = this.props;
    return (
      <PanGestureHandler
        enabled={!this.state.disabled}
        onGestureEvent={this.onPanGestureEvent}
        onHandlerStateChange={this.onPanHandlerStateChange}
      >
        <Animated.View style={{ flex: 1 }}>
          <PinchGestureHandler
            onGestureEvent={this.onPinchGestureEvent}
            onHandlerStateChange={this.onPinchHandlerStateChange}
          >
            <Animated.View>
              <Animated.Image
                source={imageSource}
                style={{
                  resizeMode: Image.resizeMode.contain,
                  width,
                  height,
                  transform: [
                    { scale: this.interScale },
                    { translateX: this.interTranslateX },
                    { translateY: this.interTranslateY },
                  ],
                }}
              />
            </Animated.View>
          </PinchGestureHandler>
        </Animated.View>
      </PanGestureHandler>
    );
  }
}

@osdnk
Copy link
Contributor

osdnk commented Jul 16, 2018

...so this example is working and you wish to remove this.state = { disabled: false };?

@farkob
Copy link
Author

farkob commented Jul 16, 2018

No no no. I meant if you set this.state.disabled = true from the get-go, PanGestureHandler does not respond to touches, as expected.

Actually, I made a snack to make it easier to see. Then I realized it was working as expected. After testing everything I realized if I had it wrapped in a Modal, as in the snack it right now, it stopped working as expected. You can try it here. Pinch and zoom the image, then pan it to left until you hit the bound, you will see that it will stop responding as expected. But then move your finger away and try panning again, you can pan it! If you change the wrapper to be a View it won't pan after its disabled. https://snack.expo.io/@farkob/pangesturehandler-disabled.

@osdnk
Copy link
Contributor

osdnk commented Jul 17, 2018

@farkob,
I strongly discourage you to use Modal with RNGH.
We suppose it should be fixed some day but it seems to be quite complicated issue

@osdnk
Copy link
Contributor

osdnk commented Jul 17, 2018

#139

As you see, we have modals on radar, but frankly speaking, we cannot find out what's the problem 😢

@osdnk
Copy link
Contributor

osdnk commented Aug 21, 2018

For now I decided to close this issue as it is related to another one

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants