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

pointerEvents="none" doesn't work for all SVG elements, weird behaviour for the SVG elements touch accessibility #1290

Closed
oleksandr-dziuban opened this issue Feb 18, 2020 · 20 comments

Comments

@oleksandr-dziuban
Copy link

oleksandr-dziuban commented Feb 18, 2020

Bug

Scenario 1:
pointerEvents="none" doesn't work with react-native-svg SVG elements.
For example, when we have rectangle on top of all elements and want to disable touchable behaviour for it to get an access to inner SVG elements we can't click on any SVG element, because Rectangle still blocks clicks.

Scenario 2:
For example, we are trying to find a workaround for Scenario 1 to run touch events on elements under the top SVG Rectangle, and we just drow 4 Lines to emulate Rectangle.
Lines should not block click events, as it is not a complete shape. But 4 Lines still block clicks.
Even if we draw 2 Lines (top and right side) we can't click on SVG element on the top-right area on this SVG element. Looks like 2 Lines block click events on top-right triangle area.

I'm using PanResponder, not just simple onPress events.
Both issues are available on iOS/Android.
pointerEvents="box-none" doesn't work toо and even throws an exception.

Unexpected behavior

react-native init CleanProject
cd CleanProject/
yarn add react-native-svg
cd ios && pod install && cd ..
modify App.js -> run the app

Use this code in App.js:

Scenario 1 with simple onPress callbacks:

    <G>
      <G>
        <Image
          onPress={() => { alert('<Image> onPress'); }}
          x={50}
          y={50}
          width={400}
          height={400}
          href={{ uri: 'https://interactive-examples.mdn.mozilla.net/media/examples/grapefruit-slice-332-332.jpg' }}/>
      </G>
      <G pointerEvents="none">
        <Rect
          pointerEvents="none"
          onPress={() => { alert('<Rect> onPress'); }}
          x={0}
          y={0}
          width={500}
          height={500}
          strokeWidth={10}
          stroke="#ccc"
          fill="transparent"/>
      </G>
    </G>

Scenario 1 with PanResponser callbacks:

  <G>
      <G>
        <Image
          onStartShouldSetResponder={() => true}
          onMoveShouldSetResponder={() => true}
          onMoveShouldSetPanResponderCapture={() => true}
          onStartShouldSetPanResponderCapture={() => true}
          onResponderGrant={() => alert('<Image> Grant')}
          onResponderMove={() => alert('<Image> Move')}
          x={50}
          y={50}
          width={400}
          height={400}
          href={{ uri: 'https://interactive-examples.mdn.mozilla.net/media/examples/grapefruit-slice-332-332.jpg' }}/>
      </G>
      <G pointerEvents="none">
        <Rect
          pointerEvents="none"
          onStartShouldSetResponder={() => false}
          onMoveShouldSetResponder={() => false}
          onResponderGrant={() => alert('<Rect> Grant')}
          onResponderMove={() => alert('<Rect> Move')}
          x={0}
          y={0}
          width={500}
          height={500}
          strokeWidth={10}
          stroke="#ccc"
          fill="transparent"/>
      </G>
    </G>

Scenario 2: (Only bottom-left triangle area on Image is clickable)

   <G>
      <G>
        <Image
          onStartShouldSetResponder={() => true}
          onMoveShouldSetResponder={() => true}
          onMoveShouldSetPanResponderCapture={() => true}
          onStartShouldSetPanResponderCapture={() => true}
          onResponderGrant={() => alert('<Image> Grant')}
          onResponderMove={() => alert('<Image> Move')}
          x={50}
          y={50}
          width={400}
          height={400}
          href={{ uri: 'https://interactive-examples.mdn.mozilla.net/media/examples/grapefruit-slice-332-332.jpg' }}/>
      </G>
      <G pointerEvents="none">
        <Line
          pointerEvents="none"
          x1={0}
          y1={0}
          x2={500}
          y2={0}
          strokeWidth={10}
          stroke="#ccc"/>
        <Line
          pointerEvents="none"
          x1={500}
          y1={0}
          x2={500}
          y2={500}
          strokeWidth={10}
          stroke="#ccc"/>
      </G>
    </G>

Environment info

React native info output:

System:
    OS: macOS 10.15.3
    CPU: (8) x64 Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
    Memory: 1.57 GB / 16.00 GB
    Shell: 3.2.57 - /bin/bash
  Binaries:
    Node: 12.14.1 - /usr/local/bin/node
    Yarn: 1.21.1 - /usr/local/bin/yarn
    npm: 6.13.4 - /usr/local/bin/npm
    Watchman: 4.9.0 - /usr/local/bin/watchman
  SDKs:
    iOS SDK:
      Platforms: iOS 13.2, DriverKit 19.0, macOS 10.15, tvOS 13.2, watchOS 6.1
    Android SDK:
      API Levels: 23, 25, 26, 27, 28
      Build Tools: 27.0.3, 28.0.3
      System Images: android-24 | Google Play Intel x86 Atom, android-25 | Google APIs Intel x86 Atom, android-26 | Google APIs Intel x86 Atom, android-27 | Google APIs Intel x86 Atom, android-27 | Google Play Intel x86 Atom, android-28 | Google APIs Intel x86 Atom, android-28 | Google Play Intel x86 Atom, android-29 | Google APIs Intel x86 Atom
  IDEs:
    Android Studio: 3.5 AI-191.8026.42.35.6010548
    Xcode: 11.3.1/11C504 - /usr/bin/xcodebuild
  npmPackages:
    @react-native-community/cli: 3.0.4 => 3.0.4 
    react: 16.9.0 => 16.9.0 
    react-native: 0.61.5 => 0.61.5 
  npmGlobalPackages:
    react-native-cli: 2.0.1

Library version: 11.0.1

Steps To Reproduce

  1. Click on image on all scenarios.

Describe what you expected to happen:

  1. Image should be clickable in all scenarios with simple onPress or PanResponder callbacks.

@msand Could you please check this issue?
I know that pointerEvents issues were added and closed many times, but this is the very important feature on complex SVG panes with dynamic touch interactions with PanResponder mechanism.
We have 6 places in our project with workarounds for broken pointerEvents, but now for current case we can't do anything else. Thanks a lot.

@oleksandr-dziuban oleksandr-dziuban changed the title pointerEvents doesn't work for all SVG elements, weird behaviour for the SVG elements touch accessibility pointerEvents="none" doesn't work for all SVG elements, weird behaviour for the SVG elements touch accessibility Feb 18, 2020
@oleksandr-dziuban
Copy link
Author

Is this library still maintained? Looks like no...

@msand
Copy link
Collaborator

msand commented Feb 24, 2020

I'm too busy at work at the moment. Feel free to step in and make a pr with a fix. This library is certainly in need of more maintainers.

@oleksandr-dziuban
Copy link
Author

oleksandr-dziuban commented Feb 24, 2020

Hello @msand, it's OK, just it was interesting for me if it still maintained.
Ok, no problems :)
I really want to help with this library, but I'm only know the JS and other Front-End frameworks...
Java and Swift are not covered yet..

@mjmasn
Copy link

mjmasn commented Feb 25, 2020

Hmm yeah, we use pointerEvents="box-none" quite a bit in our SVG code, works fine on Android but on iOS it's giving this error:

UIView base class does not support pointerEvent value: box-none

Seems to be from here: https://github.com/facebook/react-native/blob/master/React/Views/RCTViewManager.m#L248

So I guess we need to implement setPointerEvents on iOS 🤔I've been using Objective C for about 5 minutes though so I can't promise anything PR-wise.

@oleksandr-dziuban
Copy link
Author

oleksandr-dziuban commented Feb 26, 2020

Hi @mjmasn Thanks for your response, yes, maybe Mr @msand could check this issue when he will not be too busy 👍

@mjmasn
Copy link

mjmasn commented Feb 26, 2020

@oleksandr-dziuban I realised for our use case it was enough to do e.g.

const pointerEvents = Platform.OS === 'android' ? {pointerEvents: 'box-none'} : null;

return <G {...pointerEvents}> ... </G>

Your use case seems different though.

There is also another bug preventing onPress from working on iOS which is to do with scale transforms 🤔 Will create a reproduction / submit a new bug report later today or tomorrow.

@oleksandr-dziuban
Copy link
Author

oleksandr-dziuban commented Feb 26, 2020

@mjmasn Hi, for me even the Android doesn't work

@oleksandr-dziuban
Copy link
Author

oleksandr-dziuban commented Mar 5, 2020

Hi @msand , I see some activity in this repo, maybe you have 30 mins to check this issue, could you please check, because our project is blocked because of this issue in 4 places... Thank you

@msand msand closed this as completed in bd78998 Mar 5, 2020
@msand msand reopened this Mar 5, 2020
@msand
Copy link
Collaborator

msand commented Mar 5, 2020

@oleksandr-dziuban Can you try the latest commit from the develop branch? I'm not sure what else will break because of this change. Especially with regards to gesture responders.

@oleksandr-dziuban
Copy link
Author

Ok, will do in a hour, thanks a lot, and sorry for interruption

andrew-schenk pushed a commit to linxsystems/react-native-svg that referenced this issue Mar 5, 2020
andrew-schenk pushed a commit to linxsystems/react-native-svg that referenced this issue Mar 5, 2020
@oleksandr-dziuban
Copy link
Author

oleksandr-dziuban commented Mar 5, 2020

@msand I have checked latest develop code. I'm using PanResponder events system and there is no changes when I apply pointerEvents="none" or "box-none" on iOS. Just we don't have error now that "box-none" value us not supported.
But <Rect>, <Path>, <G> elements still clickable and blocks clicks on SVG elements on lower layer.

On Android application is crashing when I try to load use pointerEvents property

@msand
Copy link
Collaborator

msand commented Mar 6, 2020

Hmm, strange, for me all three of your examples work. Did you rebuild the native code?

@oleksandr-dziuban
Copy link
Author

oleksandr-dziuban commented Mar 6, 2020

@msand Yes, I have rebuilt it yes... Looks like a few guys have same issues

@msand
Copy link
Collaborator

msand commented Mar 7, 2020

import * as React from 'react';
import Svg, {Rect, Image, G} from 'react-native-svg';
export default class App extends React.Component {
  render() {
    return (
      <Svg viewBox="0 0 500 500">
        <G>
          <G>
            <Image
              onPress={() => {
                alert('<Image> onPress');
              }}
              x={50}
              y={50}
              width={400}
              height={400}
              href={{
                uri:
                  'https://interactive-examples.mdn.mozilla.net/media/examples/grapefruit-slice-332-332.jpg',
              }}
            />
          </G>
          <G pointerEvents="none">
            <Rect
              pointerEvents="none"
              x={0}
              y={0}
              width={500}
              height={500}
              strokeWidth={10}
              stroke="#ccc"
              fill="transparent"
            />
          </G>
        </G>
      </Svg>
    );
  }
}

@msand
Copy link
Collaborator

msand commented Mar 7, 2020

import * as React from 'react';
import Svg, {Rect, Image, G} from 'react-native-svg';
export default class App extends React.Component {
  render() {
    return (
      <Svg viewBox="0 0 500 500">
        <G>
          <G>
            <Image
              onStartShouldSetResponder={() => true}
              onMoveShouldSetResponder={() => true}
              onMoveShouldSetPanResponderCapture={() => true}
              onStartShouldSetPanResponderCapture={() => true}
              onResponderGrant={() => alert('<Image> Grant')}
              onResponderMove={() => alert('<Image> Move')}
              x={50}
              y={50}
              width={400}
              height={400}
              href={{
                uri:
                  'https://interactive-examples.mdn.mozilla.net/media/examples/grapefruit-slice-332-332.jpg',
              }}
            />
          </G>
          <G pointerEvents="none">
            <Rect
              pointerEvents="none"
              onStartShouldSetResponder={() => false}
              onMoveShouldSetResponder={() => false}
              onResponderGrant={() => alert('<Rect> Grant')}
              onResponderMove={() => alert('<Rect> Move')}
              x={0}
              y={0}
              width={500}
              height={500}
              strokeWidth={10}
              stroke="#ccc"
              fill="transparent"
            />
          </G>
        </G>
      </Svg>
    );
  }
}

@msand
Copy link
Collaborator

msand commented Mar 7, 2020

import * as React from 'react';
import Svg, {Line, Image, G} from 'react-native-svg';
export default class App extends React.Component {
  render() {
    return (
      <Svg viewBox="0 0 500 500">
        <G>
          <G>
            <Image
              onStartShouldSetResponder={() => true}
              onMoveShouldSetResponder={() => true}
              onMoveShouldSetPanResponderCapture={() => true}
              onStartShouldSetPanResponderCapture={() => true}
              onResponderGrant={() => alert('<Image> Grant')}
              onResponderMove={() => alert('<Image> Move')}
              x={50}
              y={50}
              width={400}
              height={400}
              href={{
                uri:
                  'https://interactive-examples.mdn.mozilla.net/media/examples/grapefruit-slice-332-332.jpg',
              }}
            />
          </G>
          <G pointerEvents="none">
            <Line
              pointerEvents="none"
              x1={0}
              y1={0}
              x2={500}
              y2={0}
              strokeWidth={10}
              stroke="#ccc"
            />
            <Line
              pointerEvents="none"
              x1={500}
              y1={0}
              x2={500}
              y2={500}
              strokeWidth={10}
              stroke="#ccc"
            />
          </G>
        </G>
      </Svg>
    );
  }
}

@msand
Copy link
Collaborator

msand commented Mar 7, 2020

All three of those give me alerts with Image like this:
Screenshot 2020-03-07 at 17 26 00

@oleksandr-dziuban
Copy link
Author

@msand Could you please deploy npm version then, and I will try to retest all cases. Thank you for you quick response!

msand pushed a commit that referenced this issue Mar 8, 2020
## [12.0.3](v12.0.2...v12.0.3) (2020-03-08)

### Bug Fixes

* **android:** default cap, join and handling of null matrix ([df4ff9c](df4ff9c))
* **ios:** [#1290](#1290) pointerEvents="none" gesture handling ([11d14fd](11d14fd))
* [#1290](#1290) pointerEvents="none" ([bd78998](bd78998))
@msand
Copy link
Collaborator

msand commented Mar 8, 2020

🎉 This issue has been resolved in version 12.0.3 🎉

The release is available on:

Your semantic-release bot 📦🚀

@msand msand added the released label Mar 8, 2020
@oleksandr-dziuban
Copy link
Author

@msand Hello, I have tested latest deployed v12.0.3, it is working perfectly on iOS and Android! Thanks lot, I'll close this issue 👍 🥇

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

3 participants