【问题标题】:Why does 'offset' exist in React Native Panresponder?为什么 React Native Panresponder 中存在“偏移”?
【发布时间】:2019-08-14 14:56:12
【问题描述】:

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

完整问题

情景

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

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

示例:

例如,如果对象从位置 0 开始,则最初动画值和偏移量都设置为 0。如果将对象拖动 100 像素,则动画值会随着您的拖动逐渐从 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,然后当您再次单击并拖动它时,继续更新动画值?

可能的解决方案:

我能想到的使用偏移量的唯一优势是,animatedValue 现在允许您跟踪“当前手势中到目前为止的距离”,而不仅仅是“到目前为止所有手势组合的总距离”。可能存在一种情况,您需要“当前手势中到目前为止的距离”值​​,所以我想知道这是否是使用偏移的唯一原因,还是有一个更根本的原因我错过了为什么我们应该一直用吗?

任何见解都会很棒。

谢谢!

【问题讨论】:

    标签: reactjs react-native animation gesture


    【解决方案1】:

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

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

    偏移的全部意义在于您不需要在单独的变量中跟踪绝对位置值。因此,鉴于您将绝对位置存储在 this._value 中,因此您怀疑偏移量的必要性是正确的。

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

    • offset + [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 来获得正确的拖动行为。

    【讨论】:

      【解决方案2】:

      因为最好让整个动画值的状态是独立的,因此您可以将其值传递给变换。当然,也许你不想要“总行驶距离”,在这种情况下,不要使用偏移量,但如果你这样做了,使用 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 对象传递给变换属性。这就是为什么您只需使用对象的内部偏移量。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-27
        • 2017-01-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多