React NativeでScrollViewの現在のスクロール位置を取得します。


115

現在のスクロール位置、または<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>

関連:この情報を取得する便利な方法を追加することを提案するGitHubの問題
マークアメリー2017

回答:


207

お試しください。

<ScrollView onScroll={this.handleScroll} />

その後:

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

5
これは、スクロール値が変更されたときに常に発生するとは限りません。たとえば、scrollviewのサイズが変更され、画面全体を一度に表示できるようになった場合、onScrollイベントが通知されない場合があります。
Thomas Parslow、2015

19
またはevent.nativeEvent.contentOffset.x、水平方向のScrollViewがある場合;)ありがとうございます。
Tieme 2016

2
これもAndroidで使用できます。
Janaka Pushpakumara 2017年

4
苛立たしいことに、scrollEventThrottle16以下に設定しない限り(すべての単一フレームのイベントを取得するため)、これが最終フレームのオフセットを報告する保証はありません。つまり、スクロールが完了した後に正しいオフセットがあることを確認します。scrollEventThrottle={16}そのパフォーマンスへの影響を設定して受け入れる必要があります。
Mark Amery 2017

@bradoyler:の最大値を知ることは可能event.nativeEvent.contentOffset.yですか?
Isaac

55

免責事項:以下は主に、React Native 0.50での私自身の実験の結果です。ScrollViewドキュメントには、現在、以下の対象と多くの情報が欠落しています。たとえばonScrollEndDrag、完全に文書化されていません。ここにあるものはすべて、文書化されていない動作に依存しているため、残念ながら、この情報が1年後または1か月後も正しいままであることを約束することはできません。

また、以下のすべては、対象とするyオフセットを持つ純粋な垂直スクロールビューを想定しています。必要に応じて、xオフセットに変換することは、読者にとって簡単な練習になると思います。


ScrollViewテイクのさまざまなイベントハンドラーにより、を使用しeventて現在のスクロール位置を取得できますevent.nativeEvent.contentOffset.y。以下に詳述するように、これらのハンドラーの一部は、AndroidとiOSで動作がわずかに異なります。

onScroll

Androidの場合

ユーザーがスクロールしている間すべてのフレーム、ユーザーがスクロールビューを離した後にスクロールビューが滑空している間すべてのフレーム、スクロールビューが静止した最後のフレーム、およびフレームの結果としてスクロールビューのオフセットが変更されたときに起動します。変化する(例:横から縦への回転による)。

iOSの場合

ユーザーがドラッグしているとき、またはスクロールビューがグライドしているscrollEventThrottleときに発生しscrollEventThrottle={16}ます。特定の頻度で決定され、フレームごとに最大1回発生します。場合は、ユーザーがスクロールビューを解放することがグライドするのに十分な勢いを持っていながら、onScrollハンドラはまた火がするときには、滑空後に静止するでしょう。ユーザがドラッグし、それが静止している間、スクロールビューを解放する場合は、onScrollされていない場合を除き、最終的な位置のために火を保証するscrollEventThrottleように設定されていることonScroll火災スクロールのすべてのフレーム。

scrollEventThrottle={16}大きな値に設定することで削減できる設定には、パフォーマンスコストがあります。ただし、これはonScrollすべてのフレームを起動するわけではないことを意味します。

onMomentumScrollEnd

グライド後にスクロールビューが停止したときに発生します。ユーザーがスクロールビューを動かないように静止しているときにユーザーがスクロールビューを離しても、まったく起動しません。

onScrollEndDrag

ユーザーがスクロールビューのドラッグを停止したときに発生します-スクロールビューが静止したままであるか滑空し始めたかに関係なく。


これらの動作の違いを考えると、オフセットを追跡する最善の方法は、正確な状況によって異なります。最も複雑なケース(ScrollView回転によるのフレームの変更の処理を含むAndroidとiOSをサポートする必要があり、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%確実ではありません。しかし、それは私が思いつくことができる最高のものであり、この機能がフレームワーク自体に追加されるまで、これは誰でもできる最高のものだと思います。


19

ブラッド・オイラーの答えは正しいです。ただし、受け取るイベントは1つだけです。スクロール位置を常に更新する必要がある場合は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 1つだけ。


12
これは誤りです。一例として、scrollEvenThrottleはiOSでのみ機能し、Androidでは機能しません。次に、onScroll呼び出しを受け取る頻度のみを規制します。デフォルトは、1つのイベントではなく、フレームごとに1回です。1を指定すると更新が多くなり、16を指定すると少なくなります。デフォルトは0です。この答えは完全に間違っています。facebook.github.io/react-native/docs/...
Teodors

1
AndroidがReact Nativeでサポートされる前に私はこれに答えました。そのため、答えはiOSにのみ適用されます。デフォルト値はゼロで、ビューがスクロールされるたびに1回だけスクロールイベントが送信されます。
cutemachine

1
function(event:Object){}のes6表記はどうなりますか?
cbartondock

1
ただfunction(event) { }
cutemachine

1
@Teodorsこの回答はAndroidをカバーしていませんが、あなたの批判の残りは完全に混乱しています。「より少ない」更新提供されscrollEventThrottle={16}ませ。1から16の範囲内の任意の数は、更新の可能な最大速度を提供します。デフォルトの0では、ユーザーがスクロールを開始したときに(iOSでは)1つのイベントが発生し、その後の更新はありません(これはかなり役に立たず、おそらくバグです)。デフォルトは「フレームごとに1回」ではありません。コメント内のこれら2つのエラーに加えて、他のアサーションは、回答が言うことと矛盾しません(ただし、エラーがあると考えているようです)。
マークアメリー2018年

7

ユーザーがスクロールするたびに現在の位置を追跡するのではなく、単に現在の位置(たとえば、ボタンが押されたとき)を取得したい場合は、onScrollリスナーを呼び出すとパフォーマンスの問題が発生します。現在、現在のスクロール位置を簡単に取得する最もパフォーマンスの高い方法は、react-native-invokeパッケージを使用することです。この正確な例はありますが、パッケージは他の複数のことを行います。

それについてはこちらをお読みください。 https://medium.com/@talkol/invoke-any-native-api-directly-from-pure-javascript-in-react-native-1fb6afcdf57d#.68ls1sopd


2
オフセットを要求するためのよりクリーンな方法(少しハックが少ない)が現在存在するかどうか、またはこれがまだあなたが知っている最善の方法ですか?(この回答が理にかなっているように見える唯一の回答であるため、なぜこの回答が支持されないのでしょうか)
cglacet

1
パッケージはもうありません、どこかに移動しましたか?githubのは404表示されている
Noitidart

6

元の質問が要求していたスクロールが終了した後に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>

-1; onMomentumScrollEndユーザーが手放したときにスクロールビューに勢いがある場合にのみトリガーされます。スクロールビューが動いていないときにそれらが解放されると、勢いのイベントが発生することはありません。
マークアメリー

1
私はonMomentumScrollEndをテストしましたが、トリガーしないようにすることは不可能でした。さまざまな方法でスクロールしてみました。スクロールが最終位置にあるときにタッチを解放しようとしてもトリガーされました。したがって、私がテストできる限り、この答えは正しいです。
fermmm

6

上記の回答はonScroll、さまざまなAPI 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
   }
 };

ここに画像の説明を入力してください

iOSでは、ScrollViewと可視領域の関係は次のとおりです。 写真はhttps://www.objc.io/issues/3-views/scroll-view/から取得しています

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


現在のアイテムの「インデックス」を取得したい場合は、これが正解です。あなたはevent.nativeEvent.contentOffset.x / deviceWidthを取ります。ご協力いただきありがとうございます!
サライネスカルデロン

1

これが、現在のスクロール位置をビューからライブで取得する方法です。私がしているのは、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} />
    )
    }
    }

パフォーマンスを考慮してonScrollの状態を更新するのは良いですか?
Hrishi

1

onScrollを使用すると、無限ループに入ります。代わりにonMomentumScrollEndまたはonScrollEndDragを使用できます


0

ページについては、基本的に上記のメソッドを使用してこれを正確に行う高次コンポーネントに取り組んでいます。最初のレイアウトやコンテンツの変更などの微妙な点に取り掛かるのに実際には少し時間がかかります。私はそれを「正しく」行ったとは主張しませんが、ある意味では、これを慎重かつ一貫して行うコンポーネントを使用する正しい答えを検討します。

参照:react-native-paged-scroll-view。私がすべて間違ってしまったとしても、フィードバックが大好きです!


1
これは素晴らしいです。これを作ってくれてありがとう これはすぐにとても便利だと思いました。
Marty Cortez、2016年

うれしいです!フィードバックを本当に感謝します!

1
プロジェクトが保守されておらず、コンポーネントに「いくつかの醜いバグ」があることをプロジェクトが公然と認めているため、謝罪-1 。
Mark Amery

@MarkAmeryのフィードバックに感謝します。:)オープンソースの時間を見つけるのは簡単ではありません。

-3

私はcontentOffsetあなたに左上のスクロールオフセットを含むオブジェクトを与えると信じています:

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


4
これはゲッターではなく、セッターではありませんか?
アーロンワン

1
-1これはナンセンスです。リンクしたドキュメントはJSXプロップ用であり、JavaScriptプロパティではありません。オフセットを設定するためにのみ使用でき、オフセットを取得するために使用することはできません。
マークアメリー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.