我的示例代码是在 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,
},
});
非常感谢!
我不确定这有多重要,但您没有按照建议的方式添加事件侦听器: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
现在,看起来代码正在执行以下操作:
将“动画偏移”设置为等于“值偏移”(onPanResponderGrant)
将“动画偏移”设置为等于“dx”“dy”(onPanResponderMove)
将“值”设置为等于“动画值”(事件监听器)
将“动画值”设置为等于“值”(onPanResponderRelease)
在步骤一和步骤二之间,您设置了两次偏移量而没有展平(同样,不确定这有多重要)。
在第三步和第四步之间,您将“值偏移”设置为等于“动画值偏移”,然后将“动画值偏移”再次设置为等于“值偏移” - 似乎是多余的
我写了一个新版本并得到了结果。
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中。
最初,我在onPanResponderGrant中设置了偏移量,并在onPanResponderReleased中展平了偏移量,但设置偏移量后我没有执行setValue({x:0, y:0})。这导致下次按下View时不移动手指(只需按下)会出现异常移动,通过添加setValue({x:0, y:0})修复了这个问题。
我想问一下为什么设置了offset后还要将值设置为{x:0, y:0}?