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

locationX of touch events calculated differently for Android and iOS with new architecture of react native [expo 52] #2567

Open
raphael-ott opened this issue Dec 6, 2024 · 4 comments

Comments

@raphael-ott
Copy link

Description

I have added an onPress eventhandler to a Circle element, which should determine the current x position of the circle in relation to the surrounding Svg element when it is clicked:

<Svg><Circle r={50} cx={100} cy={100} onPress={(e) => { alert(e.nativeEvent.locationX); }}/></Svg>

Before upgrading to Expo 52 with react-native 0.76.3 (new arch enabled) everything worked fine. When I clicked the Circle the locationX was calculated relatively to the surrounding Svg. In the example above it would have prompted me with "100" when clicked in the center of the Circle both on iOS and Android. Now with the new arch of react native enabled it is promting "50" on iOS, which is not what I am expecting. On Android it still works fine with the expected value of "100".

Steps to reproduce

  1. Add the code from above to a component of your choice
  2. Run it on iOS
  3. Read the result from the alert promt
  4. Repeat on Android

Snack or a link to a repository

https://snack.expo.dev/@raphael.ott/svg-locationx

SVG version

15.8.0

React Native version

0.76.3

Platforms

Android, iOS

JavaScript runtime

None

Workflow

Expo Go

Architecture

Fabric (New Architecture)

Build type

Debug app & production bundle

Device

iOS simulator

Device model

iPhone 16 Pro

Acknowledgements

Yes

@CaptainJeff
Copy link

CaptainJeff commented Jan 7, 2025

Noticing the same issue. @raphael-ott were you able to find a solution?

@raphael-ott
Copy link
Author

Hi @CaptainJeff,
unfortunately not. I opted out of using the new architecture for now (via app.config.ts), because this issue is currently a showstopper for me.

@CaptainJeff
Copy link

@raphael-ott

I think i figured out a solution last night. So it seems like it now calculates the Path onPress event relative to the path rather than SVG parent, like it did previously (and still does on android). So for ios, if you don't need to know which path was pressed you can just move it to <SVG onPress instead of the path. But if you need to know which path was pressed i'm doing this

  const [selected, setSelected] = useState<string>()
  const svgRef = useRef<Svg>(null)
  const pathRefs = useRef(new Map<string, any>()) // Store refs for each Path
  const [svgLayout, setSvgLayout] = useState({ x: 0, y: 0, width: 0, height: 0 })

  const handleSvgPress = async (event: GestureResponderEvent) => {
    const { locationX, locationY } = event.nativeEvent

    // Check each path to see if the point is within its fill
    for (const [type, ref] of pathRefs.current.entries()) {
      if (ref && ref.isPointInFill) {
        const isInFill = await ref.isPointInFill({
          x: locationX,
          y: locationY + svgLayout.y
        })

        if (isInFill) {
          props.selectArea({ x: locationX, y: locationY + svgLayout.y }, type)
          return
        }
      }
    }
  }

  useEffect(() => {
    setSelected(props.selectedPainArea)
  }, [props.selectedPainArea])

  return (
    <>
      <Svg
        ref={svgRef}
        viewBox="0 0 296 612"
        onPress={handleSvgPress}
        onLayout={(e) => {
          const { width, height, x, y } = e.nativeEvent.layout
          setSvgLayout({ x, y, width, height })
        }}
        {...props}>
        {FrontPainItems.map((path) => (
          <Path
            key={path.type}
            d={path.d}
            ref={(ref) => pathRefs.current.set(path.type, ref)}
            fill={selected === path.type ? color.palette.red.medium : "#fff"}
            stroke={"#504d4b"}
            strokeWidth={1}
          />
        ))}
      </Svg>

Where handleSvgPress checks to see if the the click event intersects that path. It seems to work

@raphael-ott
Copy link
Author

Thanks @CaptainJeff for the insights into your approach. The isPointinFill option is a nice idea. Nevertheless, it is currently too much effort for me to rebuild in this direction, because my svg construct is rather complex. And i would also like to avoid using two different approaches for iOS and Android here.. But I will definitely keep it in my mind if there is no fix for this problem in the future!

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