Получить текущую позицию прокрутки ScrollView в React Native

можно ли получить текущую позицию прокрутки или текущую страницу <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>

8 ответов


попробовать.

<ScrollView onScroll={this.handleScroll} />

и затем:

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

отказ от ответственности: то, что следует, в первую очередь является результатом моих собственных экспериментов в React Native 0.50. The ScrollView документация в настоящее время отсутствует большая часть информации, описанной ниже; например onScrollEndDrag совершенно без документов. Поскольку все здесь зависит от недокументированного поведения, я, к сожалению, не могу обещать, что эта информация останется верной через год или даже месяц.

кроме того, все ниже предполагает чисто вертикальный scrollview чей y смещение, которое нас интересует; перевод на x смещения, если это необходимо, надеюсь, легкое упражнение для читателя.


различные обработчики событий на ScrollView взять event и позволяют получить текущую позицию прокрутки через event.nativeEvent.contentOffset.y. Некоторые из этих обработчиков имеют различное поведение между Android и iOS, как описано ниже.

onScroll

на Android

запускает каждый кадр во время прокрутки пользователя, на каждом кадре, когда вид прокрутки скользит после того, как пользователь отпускает его, на последнем кадре, когда вид прокрутки останавливается, а также всякий раз, когда смещение вида прокрутки изменяется в результате изменения его кадра (например, из-за поворота с ландшафта на портрет).

на iOS

срабатывает при перетаскивании пользователя или при скольжении вида прокрутки с определенной частотой, определяемой scrollEventThrottle и не более одного раза за кадр, когда scrollEventThrottle={16}. если пользователь выпускает вид прокрутки, пока он имеет достаточно импульса для скольжения,onScroll обработчик также срабатывает, когда дело доходит до отдыха после катания. Однако, если пользователь перетаскивает, а затем освобождает вид прокрутки, пока он неподвижен,onScroll is не гарантированный огонь для окончательной позиции, если scrollEventThrottle было установлено так, что onScroll пожары каждый кадр прокрутка.

существует стоимость производительности для установки scrollEventThrottle={16} это можно уменьшить, установив его на большее число. Однако, это означает, что onScroll не будет стрелять каждый кадр.

onMomentumScrollEnd

срабатывает, когда вид прокрутки останавливается после скольжения. Не срабатывает вообще, если пользователь отпускает вид прокрутки, пока он неподвижен, так что он не скользит.

onScrollEndDrag

срабатывает, когда пользователь останавливает перетаскивание вида прокрутки независимо от прокрутки остается неподвижным и начинает скользить.


учитывая эти различия в поведении, лучший способ отслеживать смещение зависит от ваших конкретных обстоятельств. В самом сложном случае (вам нужно поддерживать Android и iOS, включая обработку изменений в ScrollViewкадр из-за вращения, и вы не хотите принимать штраф за производительность на 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}
>

но если мы хотим обрабатывать изменения кадра (например, потому что мы разрешаем поворачивать устройство, изменяя доступную высоту для кадра прокрутки) и / или изменения содержимого, то мы должны дополнительно реализовать оба onContentSizeChange и 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% уверен, что он всегда будет работать в тех случаях, когда вы одновременно изменить размер как кадр, так и содержимое вида прокрутки. Но это лучшее, что я могу придумать, и до эта функция добавляется в самой структуре, я думаю, это лучшее, что любой может сделать.


ответ Брэда ойлера правильный. Но вы получите только одно событие. Если вам нужно получать постоянные обновления позиции прокрутки, вы должны установить scrollEventThrottle prop, вот так:

<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


Я считаю contentOffset даст вам объект, содержащий смещение прокрутки в левом верхнем углу:

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


Это должно работать как для Android, так и для iOS (протестировано на Android):

<ScrollView ref='myScrollView'>
  <Text>Blah</Text>
</ScrollView>

...

getOffset() {
  console.log(this.refs.myScrollView.scrollProperties.offset);
}

чтобы получить 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-paged-scroll-view. Хотел бы обратную связь, даже если это что я все сделал неправильно!