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

Get current scroll position of ScrollView in React Native

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

所以像这样:

<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>

我相信contentOffset会给你一个包含左上角滚动偏移量的对象:

http://facebook.github.io/react-native/docs/scrollview.html#contentoffset

试试。

<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;
  };

Brad Oyler 的回答是正确的。但是您只会收到一个事件。如果你需要不断更新滚动位置,你应该设置 scrollEventThrottle 属性,像这样:

<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 只有一个。

至于页面,我正在开发一个高阶组件,它基本上使用上述方法来完成此操作。当您深入了解初始布局和内容更改等细微之处时,实际上只需要一点时间。我不会声称已经做到了 'correctly',但在某种意义上,我认为正确的答案是使用能够谨慎且始终如一地执行此操作的组件。

参见:react-native-paged-scroll-view。希望得到反馈,即使是我做错了!

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

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

要按照原始问题的要求在滚动结束后获得 x/y,最简单的方法可能是这样的:

<ScrollView
   horizontal={true}
   pagingEnabled={true}
   onMomentumScrollEnd={(event) => { 
      // scroll animation ended
      console.log(e.nativeEvent.contentOffset.x);
      console.log(e.nativeEvent.contentOffset.y);
   }}>
   ...content
</ScrollView>

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

此外,下面的所有内容都假定为纯垂直滚动视图,我们感兴趣的是其 y 偏移量;如果需要,转换为 x 偏移量,希望对 reader.

来说是一个简单的练习

ScrollView 上的各种事件处理程序采用 event 并让您通过 event.nativeEvent.contentOffset.y 获取当前滚动位置。其中一些处理程序在 Android 和 iOS 之间的行为略有不同,详情如下。

onScroll

在 Android

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

在 iOS

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

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

onMomentumScrollEnd

滚动视图在滑动后停止时触发。如果用户在滚动视图静止时释放滚动视图使其不会滑动,则根本不会触发。

onScrollEndDrag

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


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

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

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

要额外支持 iOS,如果 你很乐意在每一帧触发 onScroll 处理程序并接受它的性能影响,并且 如果你不需要处理frame的变化,那就稍微复杂一点:

<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}
>

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

<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% 确定它在您同时 更改滚动视图的框架和内容的大小的情况下总是能正常工作。但这是我能想到的最好的,直到 this feature gets added within the framework itself,我认为这是任何人都能做到的最好的。

这就是最终从视图中实时获取当前滚动位置的方式。我所做的只是使用滚动事件侦听器和 scrollEventThrottle。然后我将它作为一个事件传递来处理我制作的滚动功能并更新我的道具。

 export default class Anim extends React.Component {
          constructor(props) {
             super(props);
             this.state = {
               yPos: 0,
             };
           }

        handleScroll(event){
            this.setState({
              yPos : event.nativeEvent.contentOffset.y,
            })
        }

        render() {
            return (
          <ScrollView onScroll={this.handleScroll.bind(this)} scrollEventThrottle={16} />
    )
    }
    }

以上答案告诉我们如何使用不同的 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/

使用onScroll进入死循环。可以改用 onMomentumScrollEnd 或 onScrollEndDrag

 <ScrollView style={{ height: hp('70%'), width: '100%' }} showsVerticalScrollIndicator={false}
              ref={myRef}

        // for getting scrollView offset ->

              onScroll={(event => setOffset(event.nativeEvent.contentOffset.y))}

        // for giving scrollView offset ->

              onContentSizeChange={() =>
                myRef.current.scrollTo({
                  x: 0,
                  y: Offset
                })
              }
            // pagingEnabled={true}
            // scrollEventThrottle={16}
            >

<滚动视图

showsVerticalScrollIndicator={false}

ref={myRef}

onScroll={(event => setOffset(event.nativeEvent.contentOffset.y))}

onContentSizeChange={() =>

myRef.current.scrollTo({

x: 0,

y: Offset

})

}