react-native: [PanResponder] onPanResponderMove locationX/locationY values incorrect

Is this a bug report?

yes

Have you read the Bugs section of the How to Contribute guide?

yes

Environment

  1. react-native -v: 0.43.4
  2. node -v: v6.9.1
  3. npm -v: 3.10.8
  4. yarn --version: 0.18.1

Then, specify:

  • Target Platform: iOS

  • Development Operating System: macOS

  • Build tools: Xcode

Steps to Reproduce

(Write your steps here:)

  1. use panResponder
  2. console.log locationX and locationY of view when I move it in onPanResponderMove

Expected Behavior

the locationX change with the view move

Actual Behavior

locationX do change when view move, but the value is not currect, just like:

locationX : 38.5 locationY : 53.5 locationX : 152.5 locationY : 278.5 locationX : 29.5 locationY : 45 locationX : 138 locationY : 264.5

Reproducible Demo

import React, {PureComponent, Component} from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    PanResponder,
} from 'react-native';

export default class TouchStartAndRelease extends PureComponent {
    constructor(props) {
        super(props);
        this.state = {
            backgroundColor: 'red',
            marginTop: 100,
            marginLeft: 100,
        }
    }

    componentWillMount(){
        this._panResponder = PanResponder.create({
            onStartShouldSetPanResponder: (evt, gestureState) => {
                return true;
            },
            onMoveShouldSetPanResponder:  (evt, gestureState) => {
                return true;
            },
            onPanResponderGrant: (evt, gestureState) => {
                this._highlight();
            },
            onPanResponderMove: (evt, gestureState) => {
                console.log(`locationX : ${evt.nativeEvent.locationX}   locationY : ${evt.nativeEvent.locationY}`);
                this.setState({
                        marginLeft: evt.nativeEvent.locationX,
                        marginTop: evt.nativeEvent.locationY,
                });
            },
            onPanResponderRelease: (evt, gestureState) => {
                this._unhighlight();
            },
            onPanResponderTerminate: (evt, gestureState) => {
            },
        });
    }

    _unhighlight(){
        this.setState({
            backgroundColor: 'red',
        });
    }

    _highlight(){
        this.setState({
            backgroundColor: 'blue',
        });
    }

    render() {
        return (
            <View style={styles.container}>
                <View style={[styles.redView,
                    {
                        backgroundColor: this.state.backgroundColor,
                        marginTop: this.state.marginTop,
                        marginLeft: this.state.marginLeft,
                    }
                ]}
                    {...this._panResponder.panHandlers}
                ></View>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
    },
    redView: {
        width: 100,
        height: 100,
    },

});

AppRegistry.registerComponent('TouchStartAndRelease', () => TouchStartAndRelease);

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 20 (5 by maintainers)

Commits related to this issue

Most upvoted comments

Regarding the issue where two clicks within a second will yield two onPanResponderGrant events, the second of which has wildly incorrect locationX/locationY values: this issue is still extant in 0.51. I should note that this seems to be distinct from the onPanResponderMove issue with locationX/locationY, but I assume they are related. I haven’t checked that one on 0.51, but I assume it’s still going on as well.

Overall, the issue is that the relative positions reported by PanResponder are impossibly to rely on. This forces developers to rely on absolute positions reported by layout calls, but since onLayout does not report absolute positions, messy measure() calls are necessary.

I do not think we should close this issue, as this problem prevents the use of PanResponder for correct relative positioning information. In my opinion, this is the kind of “wtf?” experience that turns off a lot of newcomers from React Native.

I don’t know if this has been fixed in later versions of react-native. However my rather simple workaround is based on the fact that locationX seems to be accurate in the onResponderGrant event so in the onResponderGrant handler I just do:

this.locationPageOffset = evt.nativeEvent.pageX - evt.nativeEvent.locationX;

then in the onResponderMove handler I can derive locationX from pageX like this.

const locationX = evt.nativeEvent.pageX - this.locationPageOffset

The first time I went down the onLayout handling route but found this simpler and more independent.

I am seeing the issue that your PR addressed as well, which is that on Android, onPanResponderMove doesn’t update the values of locationX/locationY.

However, there is another distinct issue, that I can repro on both iOS and Android. If you press a location twice in short succession, the second press will register incorrect (and in my case, far smaller) values of locationX and locationY.