software-mansion / react-native-svg

SVG library for React Native, React Native Web, and plain React web projects.
MIT License
7.5k stars 1.13k forks source link

onPress does not work #580

Closed NinjaXY closed 6 years ago

NinjaXY commented 6 years ago

This is the demo code in react-native-svg-example, it works when I run the demo. But when I copy this code to my own project, it draw the svg, but never respond when I press on the svg.

`class GroupExample extends Component { static title = 'Bind touch events callback on Group element with viewBox';

render () {
    return <Svg
        height="120"
        width="120"
        viewBox="0 0 240 240"
    >
        <G onPressIn={() => alert('Pressed on G')}>
            <G scale="1.4">
                <G>
                    <Circle cx="80" cy="80" r="30" fill="green" x="20" scale="1.2"/>
                    <Rect x="20" y="20" width="40" height="40" fill="yellow" />
                </G>
            </G>
        </G>
    </Svg>;
}

}`

PS: If I change the code <G onPressIn={() => alert('Pressed on G')}> to <G onPressIn={alert('Pressed on G')}> it pop up the alert window once when I run the project, but do not respond the press event either.

msand commented 6 years ago

NinjaXY try putting your onPress handler on the circle or rect instead as in: https://github.com/magicismight/react-native-svg-example/pull/7/commits/e5616d29fc707da68ea895d02a633bf78a4dd495#diff-72145b9bb2d9b248b3cc6379238dd173R173

<Svg height="120" width="120" viewBox="0 0 240 240">
    <G scale="1.4">
      <Circle
        onPress={this.alert.bind(this)}
        cx="32"
        cy="32"
        r="4.167"
        fill="blue"
      />
      <Path
        onPress={this.alert.bind(this)}
        d="M55.192 27.87l-5.825-1.092a17.98 17.98 0 0 0-1.392-3.37l3.37-4.928c.312-.456.248-1.142-.143-1.532l-4.155-4.156c-.39-.39-1.076-.454-1.532-.143l-4.928 3.37a18.023 18.023 0 0 0-3.473-1.42l-1.086-5.793c-.103-.543-.632-.983-1.185-.983h-5.877c-.553 0-1.082.44-1.185.983l-1.096 5.85a17.96 17.96 0 0 0-3.334 1.393l-4.866-3.33c-.456-.31-1.142-.247-1.532.144l-4.156 4.156c-.39.39-.454 1.076-.143 1.532l3.35 4.896a18.055 18.055 0 0 0-1.37 3.33L8.807 27.87c-.542.103-.982.632-.982 1.185v5.877c0 .553.44 1.082.982 1.185l5.82 1.09a18.013 18.013 0 0 0 1.4 3.4l-3.31 4.842c-.313.455-.25 1.14.142 1.53l4.155 4.157c.39.39 1.076.454 1.532.143l4.84-3.313c1.04.563 2.146 1.02 3.3 1.375l1.096 5.852c.103.542.632.982 1.185.982h5.877c.553 0 1.082-.44 1.185-.982l1.086-5.796c1.2-.354 2.354-.82 3.438-1.4l4.902 3.353c.456.313 1.142.25 1.532-.142l4.155-4.154c.39-.39.454-1.076.143-1.532l-3.335-4.874a18.016 18.016 0 0 0 1.424-3.44l5.82-1.09c.54-.104.98-.633.98-1.186v-5.877c0-.553-.44-1.082-.982-1.185zM32 42.085c-5.568 0-10.083-4.515-10.083-10.086 0-5.568 4.515-10.084 10.083-10.084 5.57 0 10.086 4.516 10.086 10.083 0 5.57-4.517 10.085-10.086 10.085z"
        fill="blue"
      />
    </G>
  </Svg>

Or something more like this: https://snack.expo.io/HJuqTiJHM

import React, { Component } from 'react';
import { Text, View, StyleSheet } from 'react-native';
import { Constants, Svg } from 'expo';

const { G, Circle, Path, Rect } = Svg;

class SvgToggle extends Component {
  static title = 'Tap the shapes to change state';
  state = {
    toggle: false,
  };

  render() {
    return (
      <View style={styles.container}>
        <View style={styles.border}>
          <Svg height="65" width="65">
            <G
              onPress={() =>
                this.setState({
                  toggle: !this.state.toggle,
                })}>
              <Rect x="0" y="0" width="65" height="65" fill="white" />
              <Circle cx="32" cy="32" r="4.167" fill="blue" />
              <Path
                d="M55.192 27.87l-5.825-1.092a17.98 17.98 0 0 0-1.392-3.37l3.37-4.928c.312-.456.248-1.142-.143-1.532l-4.155-4.156c-.39-.39-1.076-.454-1.532-.143l-4.928 3.37a18.023 18.023 0 0 0-3.473-1.42l-1.086-5.793c-.103-.543-.632-.983-1.185-.983h-5.877c-.553 0-1.082.44-1.185.983l-1.096 5.85a17.96 17.96 0 0 0-3.334 1.393l-4.866-3.33c-.456-.31-1.142-.247-1.532.144l-4.156 4.156c-.39.39-.454 1.076-.143 1.532l3.35 4.896a18.055 18.055 0 0 0-1.37 3.33L8.807 27.87c-.542.103-.982.632-.982 1.185v5.877c0 .553.44 1.082.982 1.185l5.82 1.09a18.013 18.013 0 0 0 1.4 3.4l-3.31 4.842c-.313.455-.25 1.14.142 1.53l4.155 4.157c.39.39 1.076.454 1.532.143l4.84-3.313c1.04.563 2.146 1.02 3.3 1.375l1.096 5.852c.103.542.632.982 1.185.982h5.877c.553 0 1.082-.44 1.185-.982l1.086-5.796c1.2-.354 2.354-.82 3.438-1.4l4.902 3.353c.456.313 1.142.25 1.532-.142l4.155-4.154c.39-.39.454-1.076.143-1.532l-3.335-4.874a18.016 18.016 0 0 0 1.424-3.44l5.82-1.09c.54-.104.98-.633.98-1.186v-5.877c0-.553-.44-1.082-.982-1.185zM32 42.085c-5.568 0-10.083-4.515-10.083-10.086 0-5.568 4.515-10.084 10.083-10.084 5.57 0 10.086 4.516 10.086 10.083 0 5.57-4.517 10.085-10.086 10.085z"
                fill="blue"
              />
            </G>
          </Svg>
        </View>
        <Text>{this.state.toggle ? 'Toggled' : 'Not Toggled'}!</Text>
      </View>
    );
  }
}

export default class App extends Component {
  render() {
    return (
      <View style={styles.container}>
        <SvgToggle />
        <Text style={styles.paragraph}>
          Change code in the editor and watch it change on your phone!
          Save to get a shareable url.
        </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    paddingTop: Constants.statusBarHeight,
    backgroundColor: '#ecf0f1',
  },
  paragraph: {
    margin: 24,
    fontSize: 18,
    fontWeight: 'bold',
    textAlign: 'center',
    color: '#34495e',
  },
  border: {
    borderRadius: 0,
    borderWidth: 1,
    borderColor: '#000000',
  },
});

Make sure you have stroke or fill color where you are pressing. Create e.g. a Rect or Circle with the color o the background for the entire area you want to be touch sensitive.

NinjaXY commented 6 years ago

Thanks a lot. @msand

I use the second method and it works.

Now I have another problem. I draw several rects and use the rotateX 70deg to make them looks three-dimensional, and they are partially overlapping,the graphics behind do not respond the onPress event, is there any way to solve this?

wechatimg9 wechatimg10

As you can see, if I put the two rect apart, they both answer the press event.

wechatimg11

but if put them together, only one works.

msand commented 6 years ago

This might be a limitation of the current touch event & hit target intersection logic. I'm not sure, as I haven't debugged nor looked at that part of the code almost at all.

https://github.com/react-native-community/react-native-svg/blob/master/android/src/main/java/com/horcrux/svg/RenderableShadowNode.java#L292

https://github.com/react-native-community/react-native-svg/blob/master/android/src/main/java/com/horcrux/svg/GroupShadowNode.java#L116

https://github.com/react-native-community/react-native-svg/blob/master/ios/RNSVGRenderable.m#L286

https://github.com/react-native-community/react-native-svg/blob/master/ios/Elements/RNSVGGroup.m#L100

I suspect it will require changes in the native code to fix properly, but might be possible to create your own https://facebook.github.io/react-native/docs/panresponder.html or perhaps drop down a layer to just https://facebook.github.io/react-native/docs/gesture-responder-system.html

Something a bit like this: https://github.com/msand/zoomable-svg/blob/88fe49b82d64419930ac4f83352b454101280bf1/index.js#L225-L282

But, with your own should respond and should capture logic, which calculates the correct bounding boxes for your shapes, takes the intersection with the touch event, and makes the topmost shape handle it.

giacomocerquone commented 5 years ago

Ok I'm tracking down this problem since it doesn't let the onpress handler of the piechart from react-native-charts lib works.

Basically if you specify x and y of a G tag the onpress handler of the single paths will not work. Also if you specify the x and y on a path component, the on press still doesn't work!

here the snack: https://snack.expo.io/@giacomocerquone/react-native-animated-graph

vadermemo commented 5 years ago

Same issue on October 2019! Any hero to the rescue?

msand commented 5 years ago

@vadermemo Can you provide a full reproduction?

AurangShah commented 3 years ago

@msand The touch does not work when two groups(<G tag) are pressed together. The touch area covers both groups. ***Android only for older versions only