Giter Site home page Giter Site logo

Comments (13)

jhalborg avatar jhalborg commented on May 26, 2024

So I'm slowly getting somewhere:

https://gyazo.com/97ef4f578b95a7c1242e4a3710a4e6da

The above result is with the following (typescript) code, most of it copy+pasted from the "draggable" and "scaleAndRotate" examples:

import { Animated, StyleSheet, View } from 'react-native';

import {
  PanGestureHandler,
  PinchGestureHandler,
  RotationGestureHandler,
  ScrollView,
  State,
} from 'react-native-gesture-handler';

const USE_NATIVE_DRIVER = false; // https://github.com/kmagiera/react-native-gesture-handler/issues/71

export class Sticker extends React.Component {
  onPanGestureEvent: (...args: any[]) => void;
  lastOffset: { x: number; y: number };
  translateY: Animated.Value;
  translateX: Animated.Value;
  onTiltGestureEvent: (...args: any[]) => void;
  lastTilt: number;
  tiltStr: Animated.AnimatedInterpolation;
  tilt: Animated.Value;
  onRotateGestureEvent: (...args: any[]) => void;
  lastRotate: number;
  rotateStr: Animated.AnimatedInterpolation;
  rotate: Animated.Value;
  onPinchGestureEvent: (...args: any[]) => void;
  lastScale: number;
  scale: Animated.AnimatedMultiplication;
  pinchScale: Animated.Value;
  baseScale: Animated.Value;
  constructor(props) {
    super(props);

    /* Pinching */
    this.baseScale = new Animated.Value(1);
    this.pinchScale = new Animated.Value(1);
    this.scale = Animated.multiply(this.baseScale, this.pinchScale);
    this.lastScale = 1;
    this.onPinchGestureEvent = Animated.event(
      [{ nativeEvent: { scale: this.pinchScale } }],
      { useNativeDriver: USE_NATIVE_DRIVER }
    );

    /* Rotation */
    this.rotate = new Animated.Value(0);
    this.rotateStr = this.rotate.interpolate({
      inputRange: [-100, 100],
      outputRange: ['-100rad', '100rad'],
    });
    this.lastRotate = 0;
    this.onRotateGestureEvent = Animated.event(
      [{ nativeEvent: { rotation: this.rotate } }],
      { useNativeDriver: USE_NATIVE_DRIVER }
    );

    /* Tilt */
    this.tilt = new Animated.Value(0);
    this.tiltStr = this.tilt.interpolate({
      inputRange: [-501, -500, 0, 1],
      outputRange: ['1rad', '1rad', '0rad', '0rad'],
    });
    this.lastTilt = 0;
    this.onTiltGestureEvent = Animated.event(
      [{ nativeEvent: { translationY: this.tilt } }],
      { useNativeDriver: USE_NATIVE_DRIVER }
    );

    /* Pan */
    this.translateX = new Animated.Value(0);
    this.translateY = new Animated.Value(0);
    this.lastOffset = { x: 0, y: 0 };
    this.onPanGestureEvent = Animated.event(
      [
        {
          nativeEvent: {
            translationX: this.translateX,
            translationY: this.translateY,
          },
        },
      ],
      { useNativeDriver: USE_NATIVE_DRIVER }
    );
  }

  onRotateHandlerStateChange = event => {
    if (event.nativeEvent.oldState === State.ACTIVE) {
      this.lastRotate += event.nativeEvent.rotation;
      this.rotate.setOffset(this.lastRotate);
      this.rotate.setValue(0);
    }
  };

  onPinchHandlerStateChange = event => {
    if (event.nativeEvent.oldState === State.ACTIVE) {
      this.lastScale *= event.nativeEvent.scale;
      this.baseScale.setValue(this.lastScale);
      this.pinchScale.setValue(1);
    }
  };

  onTiltGestureStateChange = event => {
    if (event.nativeEvent.oldState === State.ACTIVE) {
      this.lastTilt += event.nativeEvent.translationY;
      this.tilt.setOffset(this.lastTilt);
      this.tilt.setValue(0);
    }
  };

  onPanStateChange = event => {
    if (event.nativeEvent.oldState === State.ACTIVE) {
      this.lastOffset.x += event.nativeEvent.translationX;
      this.lastOffset.y += event.nativeEvent.translationY;
      this.translateX.setOffset(this.lastOffset.x);
      this.translateX.setValue(0);
      this.translateY.setOffset(this.lastOffset.y);
      this.translateY.setValue(0);
    }
  };

  render() {
    const translateX = this.translateX;
    const translateY = this.translateY;
    const panStyle = {
      transform: [{ translateX }, { translateY }],
    };
    return (
      <PanGestureHandler
        {...this.props}
        onGestureEvent={this.onPanGestureEvent}
        onHandlerStateChange={this.onPanStateChange}
        id="dragbox"
      >
        <RotationGestureHandler
          id="image_rotation"
          simultaneousHandlers="image_pinch"
          onGestureEvent={this.onRotateGestureEvent}
          onHandlerStateChange={this.onRotateHandlerStateChange}
        >
          <PinchGestureHandler
            id="image_pinch"
            simultaneousHandlers="image_rotation"
            onGestureEvent={this.onPinchGestureEvent}
            onHandlerStateChange={this.onPinchHandlerStateChange}
          >
            <Animated.View
              style={[panStyle, styles.container]}
              collapsable={false}
            >
              <Animated.Image
                style={[
                  styles.pinchableImage,
                  {
                    transform: [
                      { perspective: 200 },
                      { scale: this.scale },
                      { rotate: this.rotateStr },
                      { rotateX: this.tiltStr },
                      // { translateX: this.translateX },
                      // { translateY: this.translateY },
                    ],
                  },
                ]}
                source={{
                  uri:
                    'https://i.pinimg.com/736x/82/cf/b2/82cfb200c95adef00650e6450ef42925--pet-logo-cat-silhouette.jpg',
                }}
              />
            </Animated.View>
          </PinchGestureHandler>
        </RotationGestureHandler>
      </PanGestureHandler>
    );
  }
}

export default Sticker;

const styles = StyleSheet.create({
  container: {
    // ...StyleSheet.absoluteFillObject,
    // backgroundColor: 'black',
    position: 'absolute',
    overflow: 'hidden',
    alignItems: 'center',
    justifyContent: 'center',
  },
  pinchableImage: {
    width: 250,
    height: 250,
  },
});

The Animated.View around the image seemed redundant, though, so I tried removing it and moving the translation transform to the image itself. It's functional, but I get these weird drawing errors on pan gestures:

https://gyazo.com/c4c66bd6bcd8f034b181ff68ac895fcf

The changed code:

{/* <Animated.View
              style={[panStyle, styles.container]}
              collapsable={false}
            > */}
            <Animated.Image
              style={[
                styles.pinchableImage,
                {
                  transform: [
                    { perspective: 200 },
                    { scale: this.scale },
                    { rotate: this.rotateStr },
                    { rotateX: this.tiltStr },
                    { translateX: this.translateX },
                    { translateY: this.translateY },
                  ],
                },
              ]}
              source={{
                uri:
                  'https://i.pinimg.com/736x/82/cf/b2/82cfb200c95adef00650e6450ef42925--pet-logo-cat-silhouette.jpg',
              }}
            />
            {/* </Animated.View> */}

Any idea as to why?

from react-native-gesture-handler.

kmagiera avatar kmagiera commented on May 26, 2024

Hey @jhalborg! Glad to see how far have you managed to get with your demo app. Nice progress!

I'm just trying to understand what this issue is about and my understanding is that you managed to get around most of the gesture handler related issues. So my understanding is that you wanted to know why are you getting these visual artifacts while panning when you use Animated.Image as direct child instead of wrapping it in an Animated.View? If so then to be honest I don't know why this could be happening and would like to know if this is somehow related to gesture handler library. Would you be able to try to start an animation for translateX/translateY value instead of panning to see if this could be caused by gesture handler or maybe just related to animating transform property?

from react-native-gesture-handler.

jhalborg avatar jhalborg commented on May 26, 2024

Sure, let me clarify šŸ˜„ . I managed to get pinch-to-zoom as well as rotate-with-two-fingers to work simultaneously, but the user can't drag/pan the image while doing the other two. The apps I mentioned (Snapchat, Messenger stories) exemplify how I think the UX should be - if the user touches the image with two fingers, he/she should be able to mutate the image on all params simultaneously (pan, zoom, rotate).

I've made a snack of the rendering issue here

from react-native-gesture-handler.

jhalborg avatar jhalborg commented on May 26, 2024

Made it work with all three gesture handlers - should have read the docs more carefully, as it clearly says that I can use an array for simultaneousHandlers

I've posted a snack here of the sticker. It's still a WIP, as I still need to figure out how to handle the rendering issue mentioned above, as well as use the native driver.

Any feedback/input is much appreciated

https://snack.expo.io/@jhalborg/sticker-example

from react-native-gesture-handler.

kmagiera avatar kmagiera commented on May 26, 2024

Great work @jhalborg ! Happy you managed to get it to work. Have you managed to validate if the rendering issue is related to gesture handler or maybe it is some problem with using transforms in react native (I suggested that you start timing Animation on one of these properties instead of connecting it to handler). If that is the case can you please close this issue?

from react-native-gesture-handler.

zachgibson avatar zachgibson commented on May 26, 2024

@jhalborg Just tried your snack on an iPhone 7+ 11.1.2 and it seems to work perfectly. No rendering issues at all. Are you testing on an Android?

from react-native-gesture-handler.

kmagiera avatar kmagiera commented on May 26, 2024

Thanks @zachgibson for trying this out.

@jhalborg can you share what simulator/device/iOS version are you seeing this on? I'm quite confident this visual issue isn't related to gesture handler library but you never know...

from react-native-gesture-handler.

zachgibson avatar zachgibson commented on May 26, 2024

@kmagiera No problem man. Iā€™m really excited about react-native-gesture-handler so I want to try and help out any way I can.

from react-native-gesture-handler.

kmagiera avatar kmagiera commented on May 26, 2024

Closing this for now @jhalborg
If you happen to have some time to revisit and give us an update feel free to reopen

from react-native-gesture-handler.

jhalborg avatar jhalborg commented on May 26, 2024

@zachgibson @kmagiera - Thanks for investigating, and sorry for being so late with the response. I've finally been allotted some more time to doing the sticker-thing, so I'm back on issue now :-)

I can't reproduce the tearing anymore, so it must have been some environment anomaly.

I think I now have something that's a passable experience for the user, except for two remaining issues

Initial placement

I've tried modifying the initial placement of the sticker component on the screen by setting the following in the constructor:

const INITIAL_X_TRANSLATION = 0;
const INITIAL_Y_TRANSLATION = -200;
const INITIAL_LAST_OFFSET = { x: INITIAL_X_TRANSLATION, y: INITIAL_Y_TRANSLATION };
this.translateX = new Animated.Value(INITIAL_X_TRANSLATION);
this.translateY = new Animated.Value(INITIAL_Y_TRANSLATION);
this.lastOffset = INITIAL_LAST_OFFSET;

This works fine on first draw, but the sticker jumps on first drag. On subsequent drags, everything is fine, and it jumps back to the 'expected' position after first release. I can't seem to find a solution to this, maybe one of you can help?

Using native driver

@kmagiera, you mentioned in another issue that in order to use native driver on nested gesture handlers, I should just add an Animated.View between each handler. I can't get this to work, it seems. In my current code, the sticker component simply disappears. Do you have a working example somewhere?

I've created a snack here, but perhaps it's more of a gist seeing as Snack seems to have problems importing this very lib at the moment.


I'd love some feedback/help :-)

EDIT: In relation to the first issue, suggestions on how to implement a "Reset position" button would also be welcome - It doesn't seem that a simple setValue works for me.

from react-native-gesture-handler.

jhalborg avatar jhalborg commented on May 26, 2024

So, I figured out a fix for initial placement, but using a different approach - JSX instead of animated coords.

Before, I had:

<View style={{ overflow: 'hidden' }} >
  <Image
    source={{
      uri: chosenImage.uri,
    }}
    style={{
      height: imageSize,
      width: imageSize,
    }}
  >
    <Sticker watermarkUrl={this.props.watermarkUrl} />
  </Image>         
</View>

But placement can instead be done by using the ImageBackground component:

<View style={{ overflow: 'hidden' }} >
  <ImageBackground
    source={{
      uri: chosenImage.uri,
    }}
    style={{
      height: imageSize,
      width: imageSize,
      justifyContent: 'center',
      alignItems: 'center',
    }}
  >
    <Sticker watermarkUrl={this.props.watermarkUrl} />
  </ImageBackground>         
</View>

This does not solve the reset button use case, though

from react-native-gesture-handler.

impactcolor avatar impactcolor commented on May 26, 2024

I'm wondering about this in the code: if (event.nativeEvent.oldState === State.ACTIVE)
Wouldn't event.nativeEvent.oldState would be undefined as it's not being declared?

from react-native-gesture-handler.

leo0grau avatar leo0grau commented on May 26, 2024

@jhalborg
Hi, I'm a little late, but I was wondering if there's a way to solve the pinch, in this case, when I try to enlarge the sticker more, it goes back to its original size and then it starts to expand
(My english is in work progress)

from react-native-gesture-handler.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    šŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. šŸ“ŠšŸ“ˆšŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ā¤ļø Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.