为什么React Native Panresponder中存在“偏移”?

时间:2019-03-24 00:25:21

标签: reactjs react-native animation gesture

TL; DR :我正在使用React Native文档中的Panresponder代码,需要帮助来理解为什么使用“偏移”值,而不是仅仅使用动画值。

TL; DR : >

完整问题

场景

我在React Native中使用Panresponder来在屏幕上拖放对象。我正在使用RN文档中的标准代码。

基本上,可拖动对象具有动画位置值。单击对象时,该动画值的偏移量将设置为动画值,而动画值将设置为零。拖动时,动画值将逐渐设置为在该手势中拖动值的大小。释放对象时,会将偏移量添加到动画值中,然后将偏移量设置为零。

示例:

例如,如果对象从位置0开始,则最初动画值和偏移都设置为0。如果将对象拖动100px,则拖动时动画值从0逐渐增加到100。释放时,零偏移量会添加到动画值中(因此什么也不会发生)。如果再次单击对象,则偏移量设置为100,并且动画值重新设置为0。如果将对象拖动另外50px,则动画值将从0增加到50。释放对象时,该100将偏移量添加到动画值中,该值将变为150,并将偏移量重置为零。

通过这种方式,动画值将始终保持当前手势中拖动的距离,偏移量将保存对象在当前拖动手势开始之前所处的位置,并且当您释放对象时,保存的偏移量值为附加到动画值上,以便当对象处于静止状态时,动画值包含该对象已被所有手势组合拖动的总距离。

代码:

这是我用来执行此操作的代码:

this.animatedValue.addListener((value) => this._value = value); // Make this._value hold the value of this.animatedValue (essentially extract the x and y values from the more complex animatedValue)
this.panResponder = PanResponder.create({
    onPanResponderGrant: () => { // When user clicks to initiate drag
        this.animatedValue.setOffset({ // Save 'distance dragged so far' in offset
            x: this._value.x,
            y: this._value.y,
        })
        this.animatedValue.setValue({ x: 0, y: 0}) // Set this.animatedValue to (0, 0) so that it will hold only 'distance so far in current gesture'
    },
    onPanResponderMove: Animated.event([ // As object is dragged, continually update animatedValue
        null, { dx: this.animatedValue.x, dy: this.animatedValue.y}
    ]),
    onPanResponderRelease: (e, gestureState) => { // On release, add offset to animatedValue and re-set offset to zero.
        this.animatedValue.flattenOffset();
    }
}

我的问题:

此代码运行良好。如果我不明白,为什么我们需要补偿?为什么我们需要在每个新手势上将动画值重新设置为零,将其值保存在偏移中,并在完成拖动后将其重新添加到动画值中?释放对象时,它最终仅保持拖动的总距离,所以为什么不只使用动画值而使用偏移量呢?在上面的示例中,为什么不只是在将动画值拖动到100px时将动画值增加到100,然后在再次单击并拖动它时继续更新动画值?

可能的解决方案:

我可以想到的使用偏移量的唯一好处是,animationValue现在可以让您跟踪“当前手势中的当前距离”,而不是“所有手势中的到目前为止的总距离”。在某些情况下,您需要“当前手势的当前距离”值,因此我想知道这是否是使用偏移量的唯一原因,还是有一个更根本的原因我想不到为什么应该一直使用它?

任何见识都会很棒。

谢谢!

2 个答案:

答案 0 :(得分:0)

因为最好将整个动画值的状态都包含在内,所以可以将其值传递给变换。当然,在这种情况下,也许您不希望“行进的总距离”很好,不要使用偏移量,但是如果您这样做,则使用AnimatedValue的偏移量是最好的解决方案。

让我向您展示为什么通过编写一个示例来跟踪触摸之间的总距离而不使用内置偏移量的原因:

this.offsetValue = {x: 0, y:0};
this.panResponder = PanResponder.create({
    onPanResponderGrant: () => { // When user clicks to initiate drag
        this.animatedValue.setValue({ x: 0, y: 0}) // Set this.animatedValue to (0, 0) so that it will hold only 'distance so far in current gesture'
    },
    onPanResponderMove: Animated.event([ // As object is dragged, continually update animatedValue
        null, { dx: this.animatedValue.x, dy: this.animatedValue.y}
    ]),
    onPanResponderRelease: (e, gestureState) => {
        // Set the offset to the current position
        this.offsetValue = {x: gestureState.dx, y: gestureState.dy}
        // Reset our animatedvalue since the offset is now all good
        this.animatedValue.setValue({ x: 0, y: 0})
    }
}

这有效,并且用更少的代码,您现在有了Animated.Value中当前触摸的原始值,如果要移动总距离,可以使用this.offsetValue。除了...如何应用它以准确获得总距离?您可能会认为您可以这样做:

<Animated.View
  style={{
    transform: [
      { translateX: this.offset.x + this.animatedValue.x },
      { translateY: this.offset.y + this.animatedValue.y },
    ],
  }}
  {...this.panResponder.panHandlers}
/>

但是这将是一个错误,因为animatedValue.x显然不是一个数字。您可以直接使用._value,但是使用Animated有什么意义呢?整个想法是,您可以将单个Animated对象传递给transform属性。这就是为什么您只使用对象的内部偏移量的原因。

答案 1 :(得分:0)

实际上,该逻辑在您使用的示例中并不正确,因为它只是使用 flattenOffset 的部分示例,并不意味着用于标准的拖放行为(请参阅下面的段落: https://animationbook.codedaily.io/flatten-offset/):

  

由于我们在onPanResponderGrant中重置了偏移量和动画值,因此这里不需要调用flattenOffset。但是,如果要触发从释放位置到另一个位置的动画,则需要flattenOffset。

偏移量的全部要点是,您无需在单独的变量中跟踪绝对位置值。因此,鉴于您将绝对位置存储在 this._value 中。

在拖动开始时,Animated.Value的x / y值从[0,0]开始,因此拖动相对于起始位置:

  • 偏移+ [0,0] =拖动开始时的绝对位置
  • 偏移量+ [x,y] =拖动结束时的绝对位置

要使下一个拖动从正确的位置开始,只需将[x,y]添加到偏移量即可,这是通过 extractOffset()完成的:

this.panResponder = PanResponder.create({
    // Allow dragging
    onStartShouldSetPanResponder: (e, gesture) => true,
    // Update position on move
    onPanResponderMove: (e, gestureState)=> {
        Animated.event([
            null,
           {dx: this.animatedValue.x, dy: this.animatedValue.y},
        ])(e, gestureState)
    },
    // Update offset once we're done moving
    onPanResponderRelease: (e, gestureState)=> {
        this.animatedValue.extractOffset();
    }
});

多亏了偏移量,您不再需要 this._value 即可获得正确的拖动行为。