繁体   English   中英

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

尝试。

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

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

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


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

onScroll

在安卓上

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

在 iOS 上

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

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

onMomentumScrollEnd

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

onScrollEndDrag

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


鉴于这些行为差异,跟踪偏移的最佳方法取决于您的具体情况。 在最复杂的情​​况下(您需要支持 Android 和 iOS,包括处理ScrollView的帧因旋转而发生的变化,并且您不想接受将scrollEventThrottle设置为 16 对 Android 造成的性能损失),并且您需要要处理滚动视图中内容的更改,那真是一团糟。

最简单的情况是,如果您只需要处理 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% 确定它在您同时更改滚动视图的框架和内容的大小的情况下总是能正常工作。 但这是我能想到的最好的,在这个特性被添加到框架本身之前,我认为这是任何人都能做到的最好的。

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

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

如果您希望简单地获取当前位置(例如,当按下某个按钮时)而不是在用户滚动时跟踪它,那么调用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>

<ScrollView 
 onMomentumScrollEnd={(event) => this.getscrollposition(event)} 
 scrollEventThrottle={16}>

显示位置

getscrollposition(e) {console.log('scroll y ', e.nativeEvent.contentOffset.y);}

这就是最终从视图中获取当前滚动位置的方式。 我所做的只是使用 on scroll 事件侦听器和 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} />
    )
    }
    }

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

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

使用onScroll进入无限循环。 可以onMomentumScrollEndonScrollEndDrag

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

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

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

<滚动视图

显示VerticalScrollIndicator={false}

参考={我的参考}

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

onContentSizeChange={() =>

myRef.current.scrollTo({

x: 0,

y: Offset

})

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM