Create a hole in react-native webview for touch events
Asked Answered
B

1

6

We are trying to repeat the same thing that was done is this plugin: https://github.com/mapsplugin/cordova-plugin-googlemaps/blob/master/README.md (How does this plugin work) but using react-native + mapbox gl(native).

The idea is simple: webview and mapview are "siblings", webview is above mapview and part of webview is transparent and mapview is shown under it. We would like any touch events that occur in this transparent area not to be captured by webview and bubble/whatever to mapview so you can touch/drag/zoom map.

Simply said: we want part(not all of it) of webview not to capture events, but to allow underlying view to capture them.

It looks like there are some methods in react native that allow to do that(conditionally control event capture) (https://facebook.github.io/react-native/docs/view.html#onstartshouldsetrespondercapture ) but we can't find any working example to test it and we can't completely understand documentation that is provided (we can't even understand should this callback be specified for parent view or child views).

So basically we just need some instrument in react-native to conditionally capture touch events.

Can anyone help us with it? Example with map/webview may be too complicated, any conditional capture of events in two views should help a lot.

Benenson answered 15/1, 2018 at 18:54 Comment(1)
Did you ever find a (complete) solution?Henri
M
5

I am not sure if this is the perfect solution but what you can do is to create an EvenEmitter and just make the Webview catch all of the clicks. The ones that should be handled by the Map, can me sent to the map itself.

I use them on screens, where multiple components might be created but they can be emitted to the parent screen. It is mostly used to handle modal screens.

import EventEmitter from 'react-native/Libraries/vendor/emitter/EventEmitter';

this._eventEmitter = new EventEmitter();

        this._eventEmitter.addListener('modal', (data) => {
            this.setState({
                visibleModal: true,
                modalData: data
            });
        });

Then you can use them like emitter.emit("modal", data)

Edit:

Here is another approach, which also shows you how you can use the methods you were asking in the question.

class Class extends Component {

    onStartShouldSetResponder = () => true;
    onEvent = (e) => {
        let object = {};
        object.locationX = e.nativeEvent.locationX;
        object.locationY = e.nativeEvent.locationY;
        object.pageY = e.nativeEvent.pageY;
        object.pageX = e.nativeEvent.pageX;
        object.target = e.nativeEvent.target;
        //object.touches = e.nativeEvent.touches; //These will trigger a cyclic error while parsing but you can access them
        //object.changedTouches = e.nativeEvent.changedTouches;
        object.identifier = e.nativeEvent.identifier;
        alert(JSON.stringify(object));

        if (object.pageX > this.state.x && object.pageX < (this.state.x + this.state.width) && object.pageY > this.state.y && object.pageY < (this.state.y + this.state.height))
            alert("inside") //Here you can trigger whatever function you need from the underlying view
    }

    onLayout = (event) => {
        this.setState({
            x: event.nativeEvent.layout.x,
            y: event.nativeEvent.layout.y,
            width: event.nativeEvent.layout.width,
            height: event.nativeEvent.layout.height,
        });
    }

    render(){

        return(    

            <ScrollView>

            <View ref={"target"} style={{
                position: 'absolute',
                height: 200,
                width: 200,
                left: 100,
                top: 100,
                backgroundColor: 'rgba(0,0,0,0.5)'
            }}
            onLayout={this.onLayout}
            />

            <View 
            onl
            onStartShouldSetResponder={this.onStartShouldSetResponder} 
            onResponderMove={this.onEvent}
            style={{
                backgroundColor: 'rgba(255,0,0,0.3)',
                width: '100%',
                height: 150
            }}/>

            <TouchableWithoutFeedback 
            onPress={this.onEvent}
            onLongPress={this.onEvent}
            style={{
                backgroundColor: 'rgba(0,255,0,0.3)',
                width: '100%',
                height: 150
            }}> 
            <View style={{height: 150, backgroundColor: 'rgba(0,255,0,0.3)'}}/>
            </TouchableWithoutFeedback>

            <View 
            onTouchStart={this.onEvent}
            onTouchEnd={this.onEvent}
            style={{
                backgroundColor: 'rgba(0,0,255,0.3)',
                width: '100%',
                height: 150
            }}> 
            </View>

            </ScrollView>

        );
    }

}

Maybe this will help you pass events to the other views

Ml answered 17/1, 2018 at 21:14 Comment(11)
Thank you for your answer! I was toying with this idea too, but can't seem to find any proper library that can properly pass not only the event itself, but also all the proper metadata: coordinates of click etc. Also map has some "complex" events like "pinch-zoom" etc. and I have concerns about emitting this "gestures" manually. Can you recommend some react library that can do it?Benenson
Your setup can grow to be actually very complex. Can you decribe a little more your use case? I mean, you can use the Map Component from react-native-maps and extend it, or you could have a webview with absolute position above the map with small width and height, etc.Ml
Recently we have made a real-estate application using Cordova. It is rather big with a lot of different components with two main sections: listing search for clients and client support(recommendations, analytics etc.). It works very vell on iOS platform (iphone6 and better) and on new Android devices, but older android devices have poor processors and WebGL support. So now we are thinking to move some components that we have(Map, lists etc.) to native elements while keeping all other(a lot!) components in WebView. Now, of cource, we can move some routes completely to Native... >nextBenenson
but sadly there are a lot of "modals" on these routes (full listing modal, filters etc.) and it would require a lot of work to completely move these routes to Native. So we want to make this kind of hybrid - we know exact "rectangle"(coordinates) on screen where a Native component should be shown and want them to be shown it under our WebView to keed the ability to open different modals with ready animation "on top" of it.Benenson
mmm I believe you should start by migrating per screen. There is a good package which is react-native-maps made by AirBnb and you can use custom callouts or pointers, so you could catch some touch events there. As for the modals, maybe you have actually have a RN modal which is basically a webview, where you can load your existing modals. In my opinion, you are better off just migrating everything from the ground up. I do have a component that for example has views on top of it, and the map answers the usual touch events and then the boxes resolve the rest. It just basically...Ml
...adding boxes with absolute positioning on top of the map. Feel free to ping me on chat (haven't use it actually) and I can show you how we do it.Ml
This was the very first thing that we tested. The complexity of different map overlays is rather hard to describe in small messages. To understand the magnitude of problem: to completely refactor &quot;map screen&quot; will take about 2 months of 5-man team. This will seriously hinder our development speed. There is a lot of different functionality on top of map.Benenson
See my edited answer. That is another way to send events to the views below. Feel free to reach out.Ml
Thank you a lot for this example!! Looks like it work with simple test cases. We will test full map integration on monday and post results here. Any way this is very helpful.Benenson
This didn't really answer our question, but it was generally helpful and we are close to solving this problem ourselfs.Benenson
@Benenson Have you found a solution? I think I'm struggling with the same issue. See #53152920Pacificism

© 2022 - 2024 — McMap. All rights reserved.