在 React Native 中获取 ScrollView 的当前滚动位置

IT技术 ios reactjs react-native
2021-04-20 02:35:27

是否可以<ScrollView>在 React Native 中获取当前滚动位置或组件的当前页面

所以像:

<ScrollView
  horizontal={true}
  pagingEnabled={true}
  onScrollAnimationEnd={() => { 
      // get this scrollview's current page or x/y scroll position
  }}>
  this.state.data.map(function(e, i) { 
      <ImageCt key={i}></ImageCt> 
  })
</ScrollView>
6个回答

尝试。

<ScrollView onScroll={this.handleScroll} />

接着:

handleScroll: function(event: Object) {
 console.log(event.nativeEvent.contentOffset.y);
},

在另一种情况下,假设您还想实现一个分页指示器,您将希望通过这样做来进一步:

<ScrollView
     onScroll={Animated.event([{ nativeEvent: { contentOffset: { x: 
     scrollX } } }], {listener: (event) => handleScroll(event)})}
     scrollEventThrottle={16}
>
   ...some content
</ScrollView>

其中 scrollX 将是您可以用于分页的动画值,您的 handleScroll 函数可以采用以下形式:

  const handleScroll = (event) => {
    const positionX = event.nativeEvent.contentOffset.x;
    const positionY = event.nativeEvent.contentOffset.y;
  };
scrollEventThrottle令人沮丧的是,除非您设置为 16 或更低(以获取每一帧的事件),否则无法保证这将报告最后一的偏移量- 这意味着要确保滚动完成后具有正确的偏移量,您需要设置scrollEventThrottle={16}并接受其性能影响。
2021-05-31 02:35:27
@bradoyler:有可能知道 的最大值event.nativeEvent.contentOffset.y吗?
2021-06-18 02:35:27
这也可以在Android中使用。
2021-06-19 02:35:27
当滚动值更改时,这不会总是触发。例如,如果调整滚动视图的大小,使其现在能够立即在屏幕上显示整个内容,您似乎并不总是会收到 onScroll 事件告诉您。
2021-06-21 02:35:27
或者event.nativeEvent.contentOffset.x如果你有一个水平的 ScrollView ;) 谢谢!
2021-06-21 02:35:27

免责声明:以下内容主要是我自己在 React Native 0.50 中的实验结果。ScrollView文档目前缺少以下涵盖的许多信息;例如onScrollEndDrag完全没有记录。由于这里的一切都依赖于未记录的行为,不幸的是,我不能保证这些信息在一年甚至一个月后仍然正确。

此外,下面的所有内容都假设我们感兴趣y偏移量是纯垂直滚动视图如果需要,转换为x偏移量对读者来说是一个简单的练习。


ScrollViewtake an上的各种事件处理程序event,让您通过event.nativeEvent.contentOffset.y. 其中一些处理程序在 Android 和 iOS 之间的行为略有不同,详情如下。

onScroll

在安卓上

在用户滚动时触发每一帧,在用户释放滚动视图后滑动时触发每一帧,当滚动视图静止时在最后一帧触发,以及当滚动视图的偏移量因其帧而改变时触发变化(例如,由于从横向旋转到纵向)。

在 iOS 上

在用户拖动或滚动视图滑动时触发,频率由 确定,scrollEventThrottle并且最多每帧一次scrollEventThrottle={16}如果用户在滚动视图有足够的滑行动量时释放它,则onScroll处理程序也会在滑行后静止时触发。但是,如果用户在静止时拖动然后释放滚动视图,onScroll不能保证在最终位置触发,除非scrollEventThrottle已设置为onScroll触发每一帧滚动。

设置的性能成本scrollEventThrottle={16}可以通过将其设置为更大的数字来降低。但是,这意味着onScroll不会每帧都触发。

onMomentumScrollEnd

当滚动视图在滑动后停止时触发。如果用户在它静止时释放滚动视图,则它根本不会触发,这样它就不会滑动。

onScrollEndDrag

当用户停止拖动滚动视图时触发 - 无论滚动视图是保持静止还是开始滑动。


鉴于这些行为差异,跟踪偏移的最佳方法取决于您的具体情况。在最复杂的情​​况下(你需要支持Android和iOS,包括处理ScrollView由于旋转导致的frame的变化,你不想接受Android上从设置scrollEventThrottle到16的性能损失),你需要处理滚动视图中内容也发生了变化,那就是一团糟。

最简单的情况是,如果您只需要处理 Android;只需使用onScroll

<ScrollView
  onScroll={event => { 
    this.yOffset = event.nativeEvent.contentOffset.y
  }}
>

为了额外支持 iOS,如果您乐于在onScroll每一帧触发处理程序并接受其性能影响,并且如果您不需要处理帧更改,那么它只是稍微复杂一点:

<ScrollView
  onScroll={event => { 
    this.yOffset = event.nativeEvent.contentOffset.y
  }}
  scrollEventThrottle={16}
>

为了减少 iOS 上的性能开销,同时仍然保证我们记录滚动视图所定位的任何位置,我们可以增加scrollEventThrottle并额外提供一个onScrollEndDrag处理程序:

<ScrollView
  onScroll={event => { 
    this.yOffset = event.nativeEvent.contentOffset.y
  }}
  onScrollEndDrag={event => { 
    this.yOffset = event.nativeEvent.contentOffset.y
  }}
  scrollEventThrottle={160}
>

但是如果我们想要处理框架变化(例如,因为我们允许设备旋转,改变滚动视图框架的可用高度)和/或内容变化,那么我们必须另外实现两者onContentSizeChangeonLayout跟踪两者的高度滚动视图的框架及其内容,从而不断计算最大可能的偏移量并推断偏移量何时因框架或内容大小更改而自动减小:

<ScrollView
  onLayout={event => {
    this.frameHeight = event.nativeEvent.layout.height;
    const maxOffset = this.contentHeight - this.frameHeight;
    if (maxOffset < this.yOffset) {
      this.yOffset = maxOffset;
    }
  }}
  onContentSizeChange={(contentWidth, contentHeight) => {
    this.contentHeight = contentHeight;
    const maxOffset = this.contentHeight - this.frameHeight;
    if (maxOffset < this.yOffset) {
      this.yOffset = maxOffset;
    }
  }}
  onScroll={event => { 
    this.yOffset = event.nativeEvent.contentOffset.y;
  }}
  onScrollEndDrag={event => { 
    this.yOffset = event.nativeEvent.contentOffset.y;
  }}
  scrollEventThrottle={160}
>

是的,这太可怕了。我也不能 100% 确定它在您同时更改滚动视图的框架和内容的大小的情况下始终正常工作但这是我能想到的最好的,在这个功能被添加到框架本身之前,我认为这是任何人都能做到的最好的。

Brad Oyler 的回答是正确的。但是您只会收到一个事件。如果您需要不断更新滚动位置,您应该设置scrollEventThrottleprops,如下所示:

<ScrollView onScroll={this.handleScroll} scrollEventThrottle={16} >
  <Text>
    Be like water my friend …
  </Text>
</ScrollView>

和事件处理程序:

handleScroll: function(event: Object) {
  console.log(event.nativeEvent.contentOffset.y);
},

请注意,您可能会遇到性能问题。相应地调整油门。16 为您提供最多更新。0 只有一个。

我在 React Native 支持 Android 之前回答了这个问题,所以是的,答案仅适用于 iOS。默认值为零,这导致每次滚动视图时滚动事件仅发送一次。
2021-05-26 02:35:27
这是不正确的。一方面,scrollEvenThrottle 仅适用于 iOS 而不适用于 android。其次,它只调节您收到 onScroll 调用的频率。默认是每帧一次,而不是一个事件。1 为您提供更多更新,16 为您提供更少更新。0 是默认值。这个答案是完全错误的。facebook.github.io/react-native/docs/...
2021-05-29 02:35:27
function(event: Object) {} 的 es6 表示法是什么?
2021-06-06 02:35:27
@Teodors 虽然这个答案不包括 Android,但您的其余批评完全令人困惑。scrollEventThrottle={16}没有给你“少”的更新; 1-16 范围内的任何数字为您提供最大可能的更新速率。当用户开始滚动时,默认值 0 为您(在 iOS 上)提供一个事件,然后没有后续更新(这是非常无用的,可能是一个错误);默认值不是“每帧一次”。除了您评论中的这两个错误之外,您的其他断言与答案所说的任何内容都不矛盾(尽管您似乎认为它们确实如此)。
2021-06-06 02:35:27
只是function(event) { }
2021-06-15 02:35:27

以上答案告诉了如何使用不同的API来获取位置,onScrollonMomentumScrollEnd等; 如果你想知道页面索引,你可以使用偏移值来计算它。

 <ScrollView 
    pagingEnabled={true}
    onMomentumScrollEnd={this._onMomentumScrollEnd}>
    {pages}
 </ScrollView> 

  _onMomentumScrollEnd = ({ nativeEvent }: any) => {
   // the current offset, {x: number, y: number} 
   const position = nativeEvent.contentOffset; 
   // page index 
   const index = Math.round(nativeEvent.contentOffset.x / PAGE_WIDTH);

   if (index !== this.state.currentIndex) {
     // onPageDidChanged
   }
 };

在此处输入图片说明

在iOS中,ScrollView与可见区域的关系如下: 图片来自https://www.objc.io/issues/3-views/scroll-view/

参考:https : //www.objc.io/issues/3-views/scroll-view/

如果您想获取当前项目的“索引”,这就是正确的答案。您采用 event.nativeEvent.contentOffset.x / deviceWidth。谢谢你的帮助!
2021-05-27 02:35:27

如果您只想获取当前位置(例如,按下某个按钮时)而不是在用户滚动时跟踪它,那么调用onScroll侦听器将导致性能问题。目前,简单地获取当前滚动位置的最高效方法是使用react-native-invoke包。这个确切的事情有一个例子,但是这个包做了很多其他的事情。

在这里阅读。 https://medium.com/@talkol/invoke-any-native-api-directly-from-pure-javascript-in-react-native-1fb6afcdf57d#.68ls1sopd

您是否知道现在是否存在一种更简洁的方式(少一点hacky)来请求偏移量,或者这仍然是您所知道的最好的方式吗?(我想知道为什么这个答案没有被赞成,因为它是唯一一个看起来有意义的答案)
2021-06-09 02:35:27
包裹不在了,是不是移动到了某个地方?Github 显示 404。
2021-06-10 02:35:27