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

问题描述 投票:0回答:3

我的示例代码是在 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,
    },
});

非常感谢!

react-native
3个回答
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)

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

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


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,
    }
});

之前的版本有一些问题,因为我不知道react-native动画。

代码有几点:

1)动画视图应该用位置:绝对视图包裹。

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

3)addListener 工作正常。

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

(所以valueXY和监听器参数不能直接赋值)

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

在flattenOffset之后,偏移量将被添加到value中。

展平偏移


0
投票

最初,我在onPanResponderGrant中设置了偏移量,并在onPanResponderReleased中展平了偏移量,但设置偏移量后我没有执行setValue({x:0, y:0})。这导致下次按下View时不移动手指(只需按下)会出现异常移动,通过添加setValue({x:0, y:0})修复了这个问题。

我想问一下为什么设置了offset后还要将值设置为{x:0, y:0}?

© www.soinside.com 2019 - 2024. All rights reserved.