让我们动起来:React Native动画简介-第3部分

如果您想完全从头开始,这里是第1部分的链接,在第1部分中,我们使用LayoutAnimation动画了单个元素在屏幕上的重新放置。

在第2部分中,我们讨论了Animated API,内插值和循环动画。

我们把东西包起来怎么样?

我们将在本系列文章中介绍的最后一件事是根据用户的手势输入为屏幕上的元素设置动画。 您可能不会立即将其视为“动画”,但是该元素不仅会自动移动。

为此,我们将使用React Native所谓的PanResponder 。 我们将使用其中之一来允许我们在屏幕上拖动Jake。

首先,让我们创建一个新的Animated.Value来保存Jake的位置并使用用户的手势对其进行更新。 因为我们要沿着X和Y轴移动Jake,所以我们将在构造函数中创建一个Animated.ValueXY ,而不仅仅是Animated.Value 。 我们不需要传递任何默认值,因为我们不需要像上一篇文章中对旋转值所做的那样对起始值进行任何假设或对其进行插值。

this._gestureValue = new Animated.ValueXY();

接下来,我们需要创建PanResponder的实例。 我们import PanResponder from 'react-native' ,并在新的_gestureValue下创建它。 它需要许多配置选项。 这直接来自React Native文档:

  this._panResponder = PanResponder.create({ 
//要求成为响应者:
onStartShouldSetPanResponder:(evt,gestureState)=> true,
onStartShouldSetPanResponderCapture:(evt,poseState)=> true,
onMoveShouldSetPanResponder:(evt,gestureState)=> true,
onMoveShouldSetPanResponderCapture:(evt,poseState)=> true,
  onPanResponderGrant:(evt,手势状态)=> { 
//手势已开始。 显示视觉反馈,以便用户知道
// 怎么了!
//现在将gestureState.d {x,y}设置为零
},
onPanResponderMove:(evt,手势状态)=> {
//最近的移动距离是gestureState.move {X,Y}
//成为响应者以来的累计手势距离为
//gestureState.d{x,y}
},
onPanResponderTerminationRequest:(evt,手势状态)=> true,
onPanResponderRelease:(evt,手势状态)=> {
//在此视图为
//响应者。 这通常表示手势成功
},
onPanResponderTerminate:(evt,gestureState)=> {
//另一个组件已成为响应者,因此此手势
//应该取消
},
onShouldBlockNativeResponder:(evt,手势状态)=> {
//返回此组件是否应阻止本机组件成为JS
//响应者。 默认情况下返回true。 目前仅在android上受支持。
返回true;
},
});

对于我们将要在此处进行的操作,其中许多都是不必要的,但我们只供参考。 我们在这里感兴趣的主要是onPanResponderMoveonPanResponderRelease

onPanResponderMove被调用为与之绑定的任何元素。 通过添加一些日志,我们可以很快看到:

  this._panResponder = PanResponder.create({ 
{...}
onPanResponderMove:(evt,手势状态)=> {
//最近的移动距离是gestureState.move {X,Y}
//成为响应者以来的累计手势距离为
//gestureState.d{x,y}
console.log(“ onPanResponder:”,poseState);
},
{...}
});

要将panResponder绑定到元素,我们将其panHandlers扩展到其中。 在这种情况下,我们将这样做到代表我们的Jake的Animated.Image ,如下所示:

  <动画图像 
来源= {杰克}
样式= {{变换:[this.getRotationAnimation()]}}
{... this._panResponder.panHandlers}
/>

在启用“远程JS调试”的情况下启动应用程序,然后在Jake的图像内单击并拖动。

因此,我们可以看到onPanResponderMove被调用很多,并且它给我们提供的onPanResponderMove是带有一些运动数据的对象。 gestureState包含以下数据:

  • stateIDstateID ID,只要屏幕上至少有一次触摸,就会持续存在
  • moveX –最近移动的触摸的最新屏幕坐标
  • moveY –最近移动的触摸的最新屏幕坐标
  • x0 –响应者授予的屏幕坐标
  • y0 –响应者授予的屏幕坐标
  • dx –自触摸开始以来手势的累计距离
  • dy –触摸开始以来手势的累计距离
  • vx –手势的当前速度
  • vy –手势的当前速度
  • numberActiveTouches –当前屏幕上的触摸数

老实说,我从未使用过其中的一些,实际上,大多数时候dxdy我的所有需求。 我相信numberActiveTouches本质上是指有多少手指与该对象进行交互,因此这可能是使捏合/缩放手势之类的事情起作用的方式。

让我们摆脱日志记录,获取dx / dy值,并使用它们使用setValue更新_gestureValue的X和Y值。

  {...} 
onPanResponderMove:(evt,手势状态)=> {
//最近的移动距离是gestureState.move {X,Y}
//成为响应者以来的累计手势距离为
//gestureState.d{x,y}
this._gestureValue.setValue({
x:gestureState.dx,
y:gestureState.dy
});
},
{...}

然后,只需将该值应用于我们Animated.Image上的转换规则,请记住该转换需要一个对象数组,每个对象包含一个转换规则。

  <动画图像 
来源= {杰克}
样式= {{
转变: [
this.getRotationAnimation(),
{translationX:this._gestureValue.x},
{translationY:this._gestureValue.y}
]
}}
{... this._panResponder.panHandlers}
/>

刷新,现在您可以拖动我们的好杰克了!

有时候,模拟器可能会运行得很慢,在真实设备上看起来会更好。

这里有一个明确的问题,对吧? 与旋转从0开始旋转时遇到的问题类似,Jake在每个新手势开始时都会跳回其原点。 我们可以用一些简单的数学来解决这个问题。 我们只需要保存每个手势结尾处的行进距离并将其应用于每个后续手势的开头即可。

为此,我们在构造函数中创建一个对象来保存偏移量: this._gestureOffset = { x: 0, y: 0 };

然后将其添加到onPanResponderMove:内部的setValue方法中onPanResponderMove:

  {...} 
onPanResponderMove:(evt,手势状态)=> {
//最近的移动距离是gestureState.move {X,Y}
//成为响应者以来的累计手势距离为
//gestureState.d{x,y}
this._gestureValue.setValue({
x:this._gestureOffset.x + gestureState.dx,
y:this._gestureOffset.y + poseState.dy
});
},
{...}

然后,在每个手势结束时,我们只需更新偏移量即可。 这将在onPanResponderRelease内部onPanResponderRelease

  onPanResponderRelease:(evt,手势状态)=> { 
//在此视图为
//响应者。 这通常表示手势成功
this._gestureOffset.x + = poseState.dx;
this._gestureOffset.y + = poseState.dy;
...
}

我们完成了! 您应该拥有一个不错的,始终可拖动的杰克! 给自己一个掌声,现在是……好吧,您不是React Native Animations的专家,但至少您会感到湿wet,这有时是最难的部分。

您可以在此处找到最终代码。

希望对您有所帮助。 如果您对其他主题有任何疑问或建议,请告诉我。 谢谢!