Obtiene la posición de desplazamiento actual de ScrollView en React Native

¿Es posible obtener la posición de desplazamiento actual, o la página actual de un componente en React Native?

Entonces algo así como:

  { // get this scrollview's current page or x/y scroll position }}> this.state.data.map(function(e, i) {  })  

Tratar.

  

Y entonces:

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

Descargo de responsabilidad: lo que sigue es principalmente el resultado de mi propia experimentación en React Native 0.50. En la documentación de ScrollView falta actualmente mucha de la información que se cubre a continuación; por ejemplo, onScrollEndDrag está completamente indocumentado. Como todo aquí depende del comportamiento indocumentado, desafortunadamente no prometo que esta información permanecerá correcta un año o incluso un mes a partir de ahora.

Además, todo lo que sigue a continuación supone una vista de desplazamiento puramente vertical cuyo desplazamiento y estamos interesados; traducir a x compensaciones, si es necesario, es un ejercicio fácil para el lector.


Varios manejadores de eventos en un ScrollView toman un event y le permiten obtener la posición de desplazamiento actual a través de event.nativeEvent.contentOffset.y . Algunos de estos controladores tienen un comportamiento ligeramente diferente entre Android e iOS, como se detalla a continuación.

onScroll

En Android

Dispara cada fotogtwig mientras el usuario se desplaza, en cada fotogtwig mientras la vista de desplazamiento se desliza después de que el usuario la suelta, en el fotogtwig final cuando la vista de desplazamiento se detiene y también cada vez que el desplazamiento de la vista de desplazamiento cambia como resultado de su fotogtwig cambio (por ejemplo, debido a la rotación de paisaje a retrato).

En iOS

Dispara mientras el usuario está arrastrando o mientras la vista de desplazamiento se desliza, en una frecuencia determinada por scrollEventThrottle y como máximo una vez por cuadro cuando scrollEventThrottle={16} . Si el usuario suelta la vista de desplazamiento mientras tiene suficiente impulso para planear, el controlador onScroll también se activará cuando descanse después de deslizarse. Sin embargo, si el usuario arrastra y luego suelta la vista de desplazamiento mientras está parado, no se garantiza que en onScroll dispare para la posición final a menos que se haya configurado onScroll modo que onScroll active cada cuadro de desplazamiento.

Hay un costo de rendimiento para configurar scrollEventThrottle={16} que se puede reducir configurándolo en un número mayor. Sin embargo, esto significa que onScroll no activará cada fotogtwig.

onMomentumScrollEnd

Se dispara cuando la vista de desplazamiento se detiene después de deslizarse. No se dispara en absoluto si el usuario suelta la vista de desplazamiento mientras está parado de manera que no se desliza.

onScrollEndDrag

Se enciende cuando el usuario deja de arrastrar la vista de desplazamiento, independientemente de si la vista de desplazamiento se deja estacionaria o comienza a deslizarse.


Dadas estas diferencias de comportamiento, la mejor forma de realizar un seguimiento del desplazamiento depende de sus circunstancias específicas. En el caso más complicado (debe admitir Android e iOS, incluido el manejo de cambios en el marco de ScrollView debido a la rotación, y no desea aceptar la penalización de rendimiento en Android al configurar scrollEventThrottle en 16), y necesita para manejar los cambios al contenido en la vista de desplazamiento también, entonces es un maldito desastre.

El caso más simple es si solo necesitas manejar Android; solo use onScroll :

  { this.yOffset = event.nativeEvent.contentOffset.y }} > 

Para admitir iOS, si está contento de onScroll controlador onScroll cada cuadro y aceptar las implicaciones de rendimiento de eso, y si no necesita manejar cambios de marco, entonces es un poco más complicado:

  { this.yOffset = event.nativeEvent.contentOffset.y }} scrollEventThrottle={16} > 

Para reducir la sobrecarga de rendimiento en iOS y al mismo tiempo garantizar que registremos cualquier posición en la que se scrollEventThrottle la vista de desplazamiento, podemos boost scrollEventThrottle y, adicionalmente, proporcionar un controlador onScrollEndDrag :

  { this.yOffset = event.nativeEvent.contentOffset.y }} onScrollEndDrag={event => { this.yOffset = event.nativeEvent.contentOffset.y }} scrollEventThrottle={160} > 

Pero si queremos manejar cambios de marcos (por ejemplo, porque permitimos que el dispositivo gire, cambiando la altura disponible para el marco de la vista de desplazamiento) y / o cambios de contenido, entonces debemos implementar tanto onContentSizeChange como onLayout para realizar un seguimiento de la altura tanto del marco de la vista de desplazamiento como de su contenido, y así calcular continuamente el máximo desplazamiento posible e inferir cuando el desplazamiento se ha reducido automáticamente debido a un cambio en el tamaño del marco o contenido:

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

Sí, es bastante horrible. Tampoco estoy 100% seguro de que siempre funcionará correctamente en los casos en que cambie simultáneamente el tamaño del marco y el contenido de la vista de desplazamiento. Pero es lo mejor que se me ocurre, y hasta que esta característica se agregue dentro del marco en sí , creo que esto es lo mejor que cualquiera puede hacer.

La respuesta de Brad Oyler es correcta. Pero solo recibirás un evento. Si necesita obtener actualizaciones constantes de la posición de desplazamiento, debe configurar el elemento scrollEventThrottle prop, así:

   Be like water my friend …   

Y el controlador de eventos:

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

Tenga en cuenta que puede encontrarse con problemas de rendimiento. Ajuste el acelerador en consecuencia. 16 te da la mayoría de las actualizaciones. 0 solo uno

Si desea simplemente obtener la posición actual (por ejemplo, cuando se presiona algún botón) en lugar de rastrearla cada vez que el usuario se desplaza, invocar a un oyente onScroll causará problemas de rendimiento. Actualmente, la forma más eficaz de obtener simplemente la posición de desplazamiento actual es mediante el uso del paquete reaction-native-invoke . Hay un ejemplo para esta cosa exacta, pero el paquete hace muchas otras cosas.

Lea sobre esto aquí. https://medium.com/@talkol/invoke-any-native-api-directly-from-pure-javascript-in-react-native-1fb6afcdf57d#.68ls1sopd

Creo que contentOffset te dará un objeto que contiene el desplazamiento de desplazamiento superior izquierdo:

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

Esto debería funcionar tanto para Android como para iOS (probado en Android):

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

En cuanto a la página, estoy trabajando en un componente de orden superior que utiliza básicamente los métodos anteriores para hacer exactamente esto. En realidad, lleva un poco de tiempo cuando se llega a las sutilezas como el diseño inicial y los cambios de contenido. No afirmaré haberlo hecho “correctamente”, pero en cierto sentido consideraría la respuesta correcta para usar un componente que haga esto de forma cuidadosa y consistente.

Ver: react-native-paged-scroll-view . Me encantaría recibir comentarios, incluso si es que lo he hecho todo mal.

Para obtener la x / y después de que el desplazamiento finalizó según lo solicitaban las preguntas originales, la forma más fácil es probablemente esta:

  { // scroll animation ended console.log(e.nativeEvent.contentOffset.x); console.log(e.nativeEvent.contentOffset.y); }}> ...content  

Esto funciona para mi. Este caso mostrará la imagen de la lista horizontal (pantalla completa)

 const height = Dimensions.get('window').height; 

  { let position = (event.nativeEvent.contentOffset.y / height); //for vertical case this.props.onPositionChanged.bind(this, position); }/>