Get current scroll position of ScrollView in React Native

后端 未结 11 1463
遇见更好的自我
遇见更好的自我 2020-11-27 11:10

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

So something like:

<         


        
相关标签:
11条回答
  • 2020-11-27 11:43

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

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

    In iOS, the relationship between ScrollView and the visible region is as follow:

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

    0 讨论(0)
  • 2020-11-27 11:44

    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.

     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} />
        )
        }
        }
    
    0 讨论(0)
  • 2020-11-27 11:45

    Disclaimer: what follows is primarily the result of my own experimentation in React Native 0.50. The ScrollView documentation is currently missing a lot of the information covered below; for instance onScrollEndDrag is 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.

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


    Various event handlers on a ScrollView take an event and 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.

    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

    Fires while the user is dragging or while the scroll view is gliding, at some frequency determined by scrollEventThrottle and at most once per frame when scrollEventThrottle={16}. If the user releases the scroll view while it has enough momentum to glide, the onScroll handler 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, onScroll is not guaranteed to fire for the final position unless scrollEventThrottle has been set such that onScroll fires every frame of scrolling.

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

    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

    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 scrollEventThrottle to 16), and you need to handle changes to the content in the scroll view too, then it's a right damn mess.

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

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

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

    <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 scrollEventThrottle and additionally provide an onScrollEndDrag handler:

    <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 onContentSizeChange and onLayout to keep track of the height of both the scroll view's frame and its contents, and thereby continually calculate the maximum possible offset and infer when the offset has been automatically reduced due to a frame or content size change:

    <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 simultaneously change 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.

    0 讨论(0)
  • 2020-11-27 11:45

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

    <ScrollView
       horizontal={true}
       pagingEnabled={true}
       onMomentumScrollEnd={(event) => { 
          // scroll animation ended
          console.log(e.nativeEvent.contentOffset.x);
          console.log(e.nativeEvent.contentOffset.y);
       }}>
       ...content
    </ScrollView>
    
    0 讨论(0)
  • 2020-11-27 11:49

    Try.

    <ScrollView onScroll={this.handleScroll} />
    

    And then:

    handleScroll: function(event: Object) {
     console.log(event.nativeEvent.contentOffset.y);
    },
    
    0 讨论(0)
提交回复
热议问题