web-dev-qa-db-fra.com

Obtenir la position de défilement actuelle de ScrollView dans React Native

Est-il possible d'obtenir la position de défilement actuelle ou la page actuelle d'un composant <ScrollView> dans React Native?

Donc, quelque chose comme:

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

Essayer.

<ScrollView onScroll={this.handleScroll} />

Et alors: 

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

Disclaimer: ce qui suit est principalement le résultat de ma propre expérience dans React Native 0.50. La documentation ScrollView manque actuellement une grande partie des informations décrites ci-dessous; par exemple, onScrollEndDrag est complètement non documenté. Étant donné que tout ici repose sur un comportement non documenté, je ne peux malheureusement rien promettre que ces informations resteront correctes dans un an, voire un mois.

En outre, tout ce qui suit est supposé être une vue de défilement purement verticale dont le décalage y nous intéresse; la traduction en x décalages, si nécessaire, est, espérons-le, un exercice facile pour le lecteur.


Différents gestionnaires d'événements sur une ScrollView prennent une 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.

onScroll

Sur Android

Déclenche chaque image pendant le défilement de l'utilisateur, sur chaque image lorsque la vue défilante défile après le relâchement de l'utilisateur, sur la dernière image lorsque la vue défilante s'immobilise et également chaque fois que le décalage de la vue défilante résulte de son cadre. changeant (par exemple en raison de la rotation de paysage en portrait).

Sur iOS

Se déclenche lorsque l'utilisateur fait glisser ou que la vue de défilement défile, à une fréquence déterminée par scrollEventThrottle et au plus une fois par image lorsque scrollEventThrottle={16}. Si l'utilisateur relâche la vue de défilement alors qu'il a suffisamment de temps pour glisser, le gestionnaire onScroll sera également déclenché lorsqu'il s'arrêtera après avoir plané. Cependant, si l'utilisateur fait glisser puis relâche la vue de défilement alors qu'elle est immobile, la variable onScroll est non garantie pour la position finale, à moins que scrollEventThrottle ne soit défini de telle sorte que onScroll déclenche chaque image du défilement.

La définition de scrollEventThrottle={16} entraîne des coûts de performance que vous pouvez réduire en définissant un nombre plus élevé. Cependant, cela signifie que onScroll ne déclenchera pas toutes les images.

onMomentumScrollEnd

Se déclenche lorsque la vue de défilement s’arrête après le vol plané. Ne se déclenche pas du tout si l'utilisateur relâche la vue de défilement alors qu'elle est immobile, 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 reste immobile ou qu'elle commence à glisser.


Compte tenu de ces différences de comportement, le meilleur moyen de suivre le décalage dépend de votre situation précise. Dans le cas le plus compliqué (vous devez prendre en charge Android et iOS, y compris la gestion des modifications du cadre de la ScrollView dues à la rotation, et vous ne voulez pas accepter la pénalité de performance sous Android du paramètre scrollEventThrottle à 16), et vous devez gérer modifie le contenu dans la vue de défilement aussi, alors c'est un foutu bordel.

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 également en charge iOS, si vous êtes prêt à déclencher le gestionnaire onScroll à chaque image et à en assumer les conséquences sur les performances, et si vous n'avez pas besoin de gérer les modifications d'image, c'est seulement un petit 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 se règle, nous pouvons augmenter scrollEventThrottle et fournir en outre un gestionnaire onScrollEndDrag:

<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 modifications de cadre (par exemple, parce que nous autorisons la rotation de l'appareil et la modification de la hauteur disponible pour le cadre de la vue de défilement) et/ou les modifications de contenu, nous devons également implémenter à la fois onContentSizeChange et onLayout pour garder une trace de la hauteur du cadre de la vue de défilement et de son contenu, et ainsi calculer en permanence le décalage possible maximum et en déduire lorsque le décalage a été automatiquement réduit en raison d'un changement de taille du cadre ou du 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 certain à 100% que cela fonctionnera toujours correctement dans les cas où vous modifiez simultanément la taille du cadre et du contenu de la vue de défilement. Mais c'est le meilleur que je puisse trouver, et jusqu'à ce que cette fonctionnalité soit ajoutée dans le cadre même , je pense que c'est le meilleur que tout le monde puisse faire.

30
Mark Amery

La réponse de Brad Oyler est correcte. Mais vous ne recevrez qu'un seul événement. Si vous devez obtenir des mises à jour constantes de la position du défilement, vous devez définir la propriété scrollEventThrottle, comme suit:

<ScrollView onScroll={this.handleScroll} scrollEventThrottle={16} >
  <Text>
    Be like water my friend …
  </Text>
</ScrollView>

Et le gestionnaire d'événement:

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

Sachez que vous pouvez rencontrer des problèmes de performances. Ajustez l'accélérateur en conséquence. 16 vous donne le plus de mises à jour. 0 un seul.

18
cutemachine

Si vous souhaitez simplement obtenir la position actuelle (par exemple, lorsque vous appuyez sur un bouton) plutôt que de la suivre chaque fois que l'utilisateur fait défiler, invoquer un écouteur onScroll entraînera des problèmes de performances. Actuellement, la méthode la plus performante pour obtenir simplement la position de défilement actuelle consiste à utiliser react-native-invoke package. Il existe un exemple pour cette chose exacte, mais le paquet fait plusieurs autres choses.

Lisez à ce sujet ici . https://medium.com/@talkol/invoke-any-native-api-directly-from-pure-javascript-in-react-native-1fb6afcdf57d#.68ls1sopd

6
Teodors

Pour obtenir le x/y après la fin du défilement comme le demandaient les questions initiales, le plus simple est probablement le suivant:

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

En ce qui concerne la page, je travaille sur un composant d'ordre supérieur qui utilise fondamentalement les méthodes ci-dessus pour faire exactement cela. En fait, cela prend un peu de temps lorsque vous abordez les subtilités telles que la modification de la présentation initiale et du contenu. Je ne prétends pas l'avoir "fait" correctement, mais dans un certain sens, je considérerais la bonne réponse pour utiliser un composant qui le fait avec soin et cohérence.

Voir: react-native-paged-scroll-view . J'adorerais les commentaires, même si c'est parce que j'ai tout faux!

0
user657199

Je crois que contentOffset vous donnera un objet contenant le décalage de défilement en haut à gauche:

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

0
Colin Ramsay

Voici comment vous avez fini par obtenir la position de défilement actuelle en direct de la vue. Tout ce que je fais est d'utiliser l'écouteur d'événements sur le défilement et le scrollEventThrottle. Je le passe alors comme un événement pour gérer la fonction de défilement que j'ai créée et mettre à jour mes accessoires. 

 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} />
    )
    }
    }
0
Sabba Keynejad