Подтвердить что ты не робот

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

Возможно ли получить текущую позицию прокрутки или текущую страницу компонента <ScrollView> в React Native?

Так что-то вроде:

<ScrollView
  horizontal={true}
  pagingEnabled={true}
  onScrollAnimationEnd={() => { 
      // get this scrollview current page or x/y scroll position
  }}>
  this.state.data.map(function(e, i) { 
      <ImageCt key={i}></ImageCt> 
  })
</ScrollView>
4b9b3361

Ответ 1

Try.

<ScrollView onScroll={this.handleScroll} />

И затем:

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

Ответ 2

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

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


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

onScroll

На Android

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

На iOS

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

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

onMomentumScrollEnd

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

onScrollEndDrag

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


Учитывая эти различия в поведении, лучший способ отслеживать смещение зависит от ваших конкретных обстоятельств. В самом сложном случае (вам необходимо поддерживать Android и iOS, включая обработку изменений в кадре ScrollView из-за вращения, и вы не хотите, чтобы scrollEventThrottle производительности на Android от установки scrollEventThrottle до 16), и вам нужно обрабатывать изменения в содержании в представлении прокрутки тоже, тогда это правильный проклятый беспорядок.

Самый простой случай: если вам нужно только обрабатывать Android; просто используйте onScroll:

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

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

Ответ 3

Ответ Брэда Ойлера правильный. Но вы получите только одно событие. Если вам нужно получать постоянные обновления положения прокрутки, вы должны установить опору 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 только один.

Ответ 4

Если вы хотите просто получить текущую позицию (например, когда нажата какая-либо кнопка), а не отслеживать ее всякий раз, когда пользователь прокручивается, вызов вызывающего пользователя onScroll приведет к проблемам с производительностью. В настоящее время наиболее эффективным способом просто получить текущую позицию прокрутки является использование react-native-invoke. Существует пример этой точной вещи, но пакет делает несколько других вещей.

Читайте об этом здесь. https://medium.com/@talkol/invoke-any-native-api-directly-from-pure-javascript-in-react-native-1fb6afcdf57d#.68ls1sopd

Ответ 5

Чтобы получить 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>

Ответ 6

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

Смотрите: react-native-paged-scroll-view. Хотелось бы получить обратную связь, даже если бы я сделал это неправильно!

Ответ 7

Так получилось, что текущая позиция прокрутки в прямом эфире отображается. Все, что я делаю, это использовать прослушиватель событий прокрутки и 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} />
    )
    }
    }

Ответ 8

Приведенные выше ответы говорят о том, как получить позицию с использованием другого API, onScroll, onMomentumScrollEnd и т. Д.; Если вы хотите узнать индекс страницы, вы можете рассчитать его, используя значение смещения.

 <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

В iOS связь между ScrollView и видимой областью следующая: The picture comes from https://www.objc.io/issues/3-views/scroll-view/

ссылка: https://www.objc.io/issues/3-views/scroll-view/

Ответ 10

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

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

...

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