software-mansion / react-native-gesture-handler

Declarative API exposing platform native touch and gesture system to React Native.
https://docs.swmansion.com/react-native-gesture-handler/
MIT License
6.13k stars 982 forks source link

Focal and anchor points are not in the coordinate space of the view on web #2929

Open j-piasecki opened 5 months ago

j-piasecki commented 5 months ago

Description

On Android and iOS the focal (for pinch) and anchor (for rotation) points are always in the coordinate space of the view, while on web they are not. It looks like they are in the window coordinate space, though I didn't investigate it that thoroughly.

https://github.com/software-mansion/react-native-gesture-handler/assets/21055725/78632cfc-24f2-43e1-8f8e-afd8a0e4b0ae

Steps to reproduce

import React from 'react';
import { StyleSheet, View } from 'react-native';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, {
  useAnimatedStyle,
  useSharedValue,
} from 'react-native-reanimated';

function Pointer(props: { x: number; y: number }) {
  return (
    <View
      style={{
        position: 'absolute',
        left: props.x,
        top: props.y,
        width: 16,
        height: 16,
        borderRadius: 8,
        backgroundColor: 'red',
        transform: [{ translateX: -8 }, { translateY: -8 }],
      }}
    />
  );
}

export default function EmptyExample() {
  const translation = useSharedValue({ x: 0, y: 0 });

  const style = useAnimatedStyle(() => {
    return {
      transform: [
        { translateX: translation.value.x },
        { translateY: translation.value.y },
      ],
    };
  });

  const [pointerPos, setPointerPos] = React.useState({ x: 100, y: 100 });
  const [pointerVisible, setPointerVisible] = React.useState(false);

  const pan = Gesture.Pan()
    .averageTouches(true)
    .onChange((e) => {
      translation.value = {
        x: translation.value.x + e.changeX,
        y: translation.value.y + e.changeY,
      };
    });

  const pinch = Gesture.Pinch()
    .onStart((e) => {
      setPointerVisible(true);
      setPointerPos({ x: e.focalX, y: e.focalY });
    })
    .onEnd(() => {
      setPointerVisible(false);
    })
    .runOnJS(true);

  const rotation = Gesture.Rotation()
    .onStart((e) => {
      setPointerVisible(true);
      setPointerPos({ x: e.anchorX, y: e.anchorY });
    })
    .onEnd(() => {
      setPointerVisible(false);
    })
    .runOnJS(true);

  return (
    <View style={styles.container}>
      <GestureDetector gesture={Gesture.Simultaneous(pan, rotation)}>
        <Animated.View
          style={[style, { width: 200, height: 200, backgroundColor: 'blue' }]}>
          {pointerVisible && <Pointer x={pointerPos.x} y={pointerPos.y} />}
        </Animated.View>
      </GestureDetector>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
});

The above snippet will render a red point at the relevant point (replace pinch/rotation in detector prop to see the other gesture). It should only change based on the position of the pointer relevant to the blue square, but it's also dependent on its position on the screen and that causes it to move outside the view.

Snack or a link to a repository

https://github.com/software-mansion/react-native-gesture-handler ❤️

Gesture Handler version

main branch

React Native version

0.74.1

Platforms

Web

JavaScript runtime

None

Workflow

Expo managed workflow

Architecture

Paper (Old Architecture)

Build type

Debug mode

Device

None

Device model

No response

Acknowledgements

Yes