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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29503252/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 05:33:43  来源:igfitidea点击:

Get current scroll position of ScrollView in React Native

iosreactjsreact-native

提问by Sang

Is it possible to get the current scroll position, or the current page of a <ScrollView>component in React Native?

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

So something like:

所以像:

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

回答by brad oyler

Try.

尝试。

<ScrollView onScroll={this.handleScroll} />

And then:

进而:

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

回答by Mark Amery

Disclaimer: what follows is primarily the result of my own experimentation in React Native 0.50. The ScrollViewdocumentationis currently missing a lot of the information covered below; for instance onScrollEndDragis completely undocumented. Since everything here relies upon undocumented behaviour, I can unfortunately make no promises that this information will remain correct a year or even a month from now.

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

Also, everything below assumes a purely vertical scrollview whose yoffset we are interested in; translating to xoffsets, if needed, is hopefully an easy exercise for the reader.

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



Various event handlers on a ScrollViewtake an eventand let you get the current scroll position via event.nativeEvent.contentOffset.y. Some of these handlers have slightly different behaviour between Android and iOS, as detailed below.

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

onScroll

onScroll

On Android

在安卓上

Fires every frame while the user is scrolling, on every frame while the scroll view is gliding after the user releases it, on the final frame when the scroll view comes to rest, and also whenever the scroll view's offset changes as a result of its frame changing (e.g. due to rotation from landscape to portrait).

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

On iOS

在 iOS 上

Fires while the user is dragging or while the scroll view is gliding, at some frequency determined by scrollEventThrottleand at most once per frame when scrollEventThrottle={16}. Ifthe user releases the scroll view while it has enough momentum to glide, the onScrollhandler will also fire when it comes to rest after gliding. However, if the user drags and then releases the scroll view while it is stationary, onScrollis notguaranteed to fire for the final position unless scrollEventThrottlehas been set such that onScrollfires every frame of scrolling.

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

There is a performance cost to setting scrollEventThrottle={16}that can be reduced by setting it to a larger number. However, this means that onScrollwill not fire every frame.

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

onMomentumScrollEnd

onMomentumScrollEnd

Fires when the scroll view comes to a stop after gliding. Does not fire at all if the user releases the scroll view while it is stationary such that it does not glide.

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

onScrollEndDrag

onScrollEndDrag

Fires when the user stops dragging the scroll view - regardless of whether the scroll view is left stationary or begins to glide.

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



Given these differences in behaviour, the best way to keep track of the offset depends upon your precise circumstances. In the most complicated case (you need to support Android and iOS, including handling changes in the ScrollView's frame due to rotation, and you don't want to accept the performance penalty on Android from setting scrollEventThrottleto 16), and you need to handle changes to the contentin the scroll view too, then it's a right damn mess.

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

The simplest case is if you only need to handle Android; just use onScroll:

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

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

To additionally support iOS, ifyou're happy to fire the onScrollhandler every frame and accept the performance implications of that, and ifyou don't need to handle frame changes, then it's only a little bit more complicated:

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

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

To reduce the performance overhead on iOS while still guaranteeing that we record any position that the scroll view settles on, we can increase scrollEventThrottleand additionally provide an onScrollEndDraghandler:

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

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

But if we want to handle frame changes (e.g. because we allow the device to be rotated, changing the available height for the scroll view's frame) and/or content changes, then we must additionally implement both onContentSizeChangeand onLayoutto keep track of the height of both the scroll view's frame and its contents, and thereby continually calculate the maximumpossible offset and infer when the offset has been automatically reduced due to a frame or content size change:

但是如果我们想要处理框架变化(例如,因为我们允许设备旋转,改变滚动视图框架的可用高度)和/或内容变化,那么我们必须另外实现两者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}
>

Yeah, it's pretty horrifying. I'm also not 100% certain that it'll always work right in cases where you simultaneouslychange the size of both the frame and content of the scroll view. But it's the best I can come up with, and until this feature gets added within the framework itself, I think this is the best that anyone can do.

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

回答by cutemachine

Brad Oyler's answer is correct. But you will only receive one event. If you need to get constant updates of the scroll position, you should set the scrollEventThrottleprop, like so:

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

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

And the event handler:

和事件处理程序:

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

Be aware that you might run into performance issues. Adjust the throttle accordingly. 16 gives you the most updates. 0 only one.

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

回答by Teodors

If you wish to simply get the current position (e.g. when some button is pressed) rather than tracking it whenever the user scrolls, then invoking an onScrolllistener is going to cause performance issues. Currently the most performant way to simply get current scroll position is using react-native-invokepackage. There is an example for this exact thing, but the package does multiple other things.

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

Read about it here. https://medium.com/@talkol/invoke-any-native-api-directly-from-pure-javascript-in-react-native-1fb6afcdf57d#.68ls1sopd

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

回答by RY Zheng

The above answers tell how to get the position using different API, onScroll, onMomentumScrollEndetc; If you want to know the page index, you can calculate it using the offset value.

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

enter image description here

在此处输入图片说明

In iOS, the relationship between ScrollView and the visible region is as follow: The picture comes from https://www.objc.io/issues/3-views/scroll-view/

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

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

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

回答by j0n

To get the x/y after scroll ended as the original questions was requesting, the easiest way is probably this:

要在滚动结束后按照原始问题的要求获取 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>

回答by Sabba Keynejad

This is how ended up getting the current scroll position live from the view. All I am doing is using the on scroll event listener and the scrollEventThrottle. I am then passing it as an event to handle scroll function I made and updating my props.

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

回答by Sabba Keynejad

As for the page, I'm working on a higher order component that uses basically the above methods to do exactly this. It actually takes just a bit of time when you get down to the subtleties like initial layout and content changes. I won't claim to have done it 'correctly', but in some sense I'd consider the correct answer to use component that does this carefully and consistently.

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

See: react-native-paged-scroll-view. Would love feedback, even if it's that I've done it all wrong!

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

回答by Colin Ramsay

I believe contentOffsetwill give you an object containing the top-left scroll offset:

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

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

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