在onPanResponderRelease之后,自动动画移动无法正常工作

时间:2018-07-11 15:26:35

标签: react-native

我的示例代码是在PanResponder的onPanResponderRelease释放之后自动移动一个距离(看起来像是物体移动惯性)。

当我拖动内容并松开手指(或在模拟器中释放拖动)时,内容正常工作(它自动移动了一段距离)。 但是当我快速触摸(或在模拟器中单击一次)时,内容会移动一段距离并返回到其他位置。这是有问题的,因为不应快速触摸内容。

出什么问题了? 这是我的代码(它可以直接粘贴并在app.js中运行。)

    import React from 'react';
import { View, StyleSheet, PanResponder, Animated, Text, Button, } from 'react-native';

export default class App extends React.Component {
    constructor(props) {
        super(props);
        this._layout = { x: 0, y: 0, width: 0, height: 0, };
        this._value = { x: 100, y: 100, };
    }

    componentWillMount() {
        this._animatedValue = new Animated.ValueXY({ x: 100, y: 100 });
        this._animatedValue.addListener((value) => {
            this._value = value;
        });

        this._panResponder = PanResponder.create({
            onStartShouldSetPanResponder: (evt, gestureState) => true,
            onMoveShouldSetPanResponder: (evt, gestureState) => ((gestureState.dx != 0) && (gestureState.dy != 0)),

            onPanResponderGrant: (e, gestureState) => {
                this._animatedValue.setOffset({ x: this._value.x, y: this._value.y });
            },

            onPanResponderMove: Animated.event([
                null,
                { dx: this._animatedValue.x, dy: this._animatedValue.y },
            ]),

            onPanResponderRelease: () => {
                this._animatedValue.flattenOffset();
                Animated.timing(
                    this._animatedValue,
                    {
                        toValue: { x: this._value.x, y: this._value.y + 100, },
                        duration: 600,
                    }
                ).start(() => {
                    // animation finished
                });
            }
        });
    }

    render() {
        return (
            <View style={styles.container}>
                <Animated.View
                    style={[
                        styles.box,
                        {
                            left: this._animatedValue.x,
                            top: this._animatedValue.y,
                        },
                    ]}
                    {...this._panResponder.panHandlers}
                >
                    <Text>{'This is a test'}</Text>
                    <Button
                        title={'Click Me'}
                        onPress={() => console.log('Yes, I clicked.')}
                    />
                </Animated.View>
            </View>
        );
    }
}

var styles = StyleSheet.create({
    container: {
        flex: 1,
    },
    box: {
        width: 200,
        height: 200,
        borderWidth: 1,
    },
});

非常感谢!

2 个答案:

答案 0 :(得分:1)

我不确定这有多重要,但是您没有按照建议的方式添加事件监听器:https://facebook.github.io/react-native/docs/animated#event

尝试像这样添加它:

componentWillMount() {
    this._animatedValue = new Animated.ValueXY({ x: 100, y: 100 });

    // REMOVE THIS
    //this._animatedValue.addListener((value) => {
    //     this._value = value;
    //});

    this._panResponder = PanResponder.create({
        onStartShouldSetPanResponder: (evt, gestureState) => true,
        onMoveShouldSetPanResponder: (evt, gestureState) => ((gestureState.dx != 0) && (gestureState.dy != 0)),

        onPanResponderGrant: (e, gestureState) => {
            this._animatedValue.setOffset({ x: this._value.x, y: this._value.y });
        },

        onPanResponderMove: Animated.event([
            null,
            { dx: this._animatedValue.x, dy: this._animatedValue.y },
        ],
            {
SEE HERE--->>>          listener: this.onMove
            }
        ),

        onPanResponderRelease: () => {
            this._animatedValue.flattenOffset();
            Animated.timing(
                this._animatedValue,
                {
                    toValue: { x: this._value.x, y: this._value.y + 100, },
                    duration: 600,
                }
            ).start(() => {
                // animation finished
            });
        }
    });
}

AND SEE HERE --->>> 

onMove() {
    var { x, y } = this.animatedValue;
    this._value.x = x;
    this._value.y = y;

}

也请尝试制作Animated.View position:absolute

现在,看起来代码正在执行此操作:

  1. 将“动画偏移”设置为等于“值偏移”(onPanResponderGrant)

  2. 将“动画偏移”设置为等于“ dx”,“ dy”(onPanResponderMove)

  3. 将“值”设置为等于“动画值”(事件监听器)

  4. 将“动画值”设置为等于“值”(onPanResponderRelease)

在步骤1和步骤2之间,您两次设置了偏移量而没有变平(同样,不确定有多重要)。

在第三步和第四步之间,您将“值偏移量”设置为“动画值偏移量”,然后将“动画值偏移量”设置为再次等于“值偏移量”-似乎是多余的

答案 1 :(得分:0)

我写了一个新版本,并得到了结果。

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

export default class App extends Component {
    constructor(props) {
        super(props);
        this._value = { x: 0, y: 0 };
        this.pan = new Animated.ValueXY({ x: 0, y: 0 });
    }

    componentWillMount() {
        this.pan.addListener((value) => {
            this._value = value;
        });

        this.panResponder = PanResponder.create({
            onStartShouldSetPanResponder: (evt, gestureState) => true,
            onMoveShouldSetPanResponder: (evt, gestureState) => ((gestureState.dx != 0) && (gestureState.dy != 0)),

            onPanResponderGrant: (e, gestureState) => {
                this.pan.setOffset({ x: this._value.x, y: this._value.y });
                this.pan.setValue({ x: 0, y: 0 });
            },

            onPanResponderMove: Animated.event([null, {
                dx: this.pan.x,
                dy: this.pan.y
            }]),
            onPanResponderRelease: (e, gesture) => {
                this.pan.flattenOffset();

                const { x, y } = this._value;
                Animated.timing(
                    this.pan,
                    {
                        toValue: { x, y: y + 50 },
                        duration: 600,
                    },
                ).start(() => {
                    // animation finished
                });
            }
        });
    }

    componentWillUnmount() {
        this.pan.removeAllListener();
    }

    render() {
        return (
            <View style={{ flex: 1 }}>
                <View style={styles.draggableContainer}>
                    <Animated.View
                        {...this.panResponder.panHandlers}
                        style={[
                            this.pan.getLayout(),
                            styles.square,
                        ]}>
                        <Text>Drag me!</Text>
                    </Animated.View>
                </View>

            </View>
        );
    }
}

let styles = StyleSheet.create({
    draggableContainer: {
        position: 'absolute',
        top: 165,
        left: 76.1,
    },
    square: {
        backgroundColor: 'red',
        width: 72,
        height: 72,
        borderWidth: 1,
    }
});

以前的版本存在一些问题,因为我不知道本机动画。

关于代码有几点:

1)动画视图应由position:绝对视图包裹。

2)动画值/ valueXY是相对位置。

3)addListener正常工作。

4)valueXY是具有两个值对象的组合对象。 但是addListener参数是具有x / y的对象,例如:{x:0,y:0}

(因此,valueXY和侦听器参数无法直接分配)

5)动画会添加值的偏移量。

flattenOffset之后,偏移量将添加到值中。

flattenOffset