Avertissement: ce qui suit est principalement le résultat de ma propre expérimentation dans React Native 0.50. La ScrollView
documentation est actuellement manque beaucoup des informations couvertes ci - dessous; par exemple onScrollEndDrag
est complètement non documenté. Puisque tout ici repose sur un comportement non documenté, je ne peux malheureusement pas promettre que ces informations resteront correctes dans un an ou même un mois.
De plus, tout ce qui suit suppose une vue de défilement purement verticale dont le décalage y nous intéresse; la conversion en décalages x , si nécessaire, est, espérons-le, un exercice facile pour le lecteur.
Divers gestionnaires d'événements sur une ScrollView
prise event
et vous permettent d'obtenir la position de défilement actuelle via event.nativeEvent.contentOffset.y
. Certains de ces gestionnaires ont un comportement légèrement différent entre Android et iOS, comme détaillé ci-dessous.
Sur Android
Déclenche chaque image pendant que l'utilisateur fait défiler, sur chaque image pendant que la vue de défilement glisse après que l'utilisateur la libère, sur l'image finale lorsque la vue de défilement s'arrête, et également chaque fois que le décalage de la vue de défilement change en raison de son cadre changeant (par exemple en raison de la rotation du paysage au portrait).
Sur iOS
Se déclenche lorsque l'utilisateur fait glisser ou lorsque la vue de défilement glisse, à une fréquence déterminée par scrollEventThrottle
et au plus une fois par image quand scrollEventThrottle={16}
. Si l'utilisateur libère la vue de défilement alors qu'il a suffisamment d'élan pour glisser, le onScroll
gestionnaire se déclenchera également lorsqu'il s'arrêtera après avoir glissé. Cependant, si l'utilisateur fait glisser puis relâche la vue de défilement alors qu'elle est stationnaire, il onScroll
n'est pas garanti de se déclencher pour la position finale à moins qu'il scrollEventThrottle
n'ait été défini de manière à onScroll
déclencher chaque image de défilement.
Il y a un coût de performance au réglage scrollEventThrottle={16}
qui peut être réduit en le définissant sur un nombre plus grand. Cependant, cela signifie que onScroll
ne déclenchera pas toutes les images.
Se déclenche lorsque la vue de défilement s'arrête après un vol plané. Ne se déclenche pas du tout si l'utilisateur libère la vue de défilement alors qu'elle est stationnaire de sorte qu'elle ne glisse pas.
onScrollEndDrag
Se déclenche lorsque l'utilisateur arrête de faire glisser la vue de défilement, que la vue de défilement soit laissée stationnaire ou qu'elle commence à glisser.
Compte tenu de ces différences de comportement, la meilleure façon de suivre le décalage dépend de vos circonstances précises. Dans le cas le plus compliqué (vous devez prendre en charge Android et iOS, y compris la gestion des changements dans le ScrollView
cadre de s en raison de la rotation, et vous ne voulez pas accepter la pénalité de performances sur Android du paramètre scrollEventThrottle
à 16), et vous devez gérer modifications du contenu dans la vue de défilement aussi, alors c'est un sacré désordre.
Le cas le plus simple est si vous avez seulement besoin de gérer Android; utilisez simplement onScroll
:
<ScrollView
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
>
Pour prendre en charge également iOS, si vous êtes heureux de déclencher le onScroll
gestionnaire à chaque image et d'en accepter les implications sur les performances, et si vous n'avez pas besoin de gérer les changements de cadre, alors ce n'est qu'un peu plus compliqué:
<ScrollView
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
scrollEventThrottle={16}
>
Pour réduire la surcharge de performances sur iOS tout en garantissant que nous enregistrons toute position sur laquelle la vue de défilement s'installe, nous pouvons augmenter scrollEventThrottle
et fournir en plus un onScrollEndDrag
gestionnaire:
<ScrollView
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
onScrollEndDrag={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
scrollEventThrottle={160}
>
Mais si nous voulons gérer les changements de cadre (par exemple, parce que nous autorisons la rotation de l'appareil, en changeant la hauteur disponible pour le cadre de la vue de défilement) et / ou les changements de contenu, nous devons également implémenter les deux onContentSizeChange
et onLayout
garder une trace de la hauteur des deux le cadre de la vue de défilement et son contenu, et ainsi calculer en permanence le décalage maximum possible et déduire quand le décalage a été automatiquement réduit en raison d'un changement de taille de cadre ou de contenu:
<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}
>
Ouais, c'est assez horrible. Je ne suis pas non plus certain à 100% que cela fonctionnera toujours correctement dans les cas où vous modifiez simultanément la taille du cadre et le contenu de la vue de défilement. Mais c'est le mieux que je puisse proposer, et jusqu'à ce que cette fonctionnalité soit ajoutée dans le cadre lui - même , je pense que c'est le mieux que quiconque puisse faire.