Получить текущую позицию прокрутки 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 62

8 ответов:

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

<ScrollView onScroll={this.handleScroll} />

и затем:

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

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

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


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

onScroll

On Android

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

на iOS

срабатывает, когда пользователь перетаскивает или когда вид прокрутки скользит, с некоторой частотой, определяемой scrollEventThrottle и не более одного раза за кадр, когда scrollEventThrottle={16}. если пользователь освобождает вид прокрутки, пока у него достаточно импульса для скольжения,onScroll обработчик также срабатывает, когда дело доходит до отдыха после катания. Однако, если пользователь перетаскивает и затем освобождает вид прокрутки, пока он неподвижен,onScroll и не гарантированный огонь для окончательной позиции, если 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 проп, вот так:

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

Что касается страницы, я работаю над компонентом более высокого порядка, который использует в основном вышеуказанные методы, чтобы сделать именно это. На самом деле это занимает всего немного времени, когда вы переходите к тонкостям, таким как начальный макет и изменения контента. Я не буду утверждать, что сделал это "правильно", но в некотором смысле я бы рассмотрел правильный ответ, чтобы использовать компонент, который делает это тщательно и последовательно.

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