web-dev-qa-db-fra.com

Comment connaître le sens de défilement d'IntersectionObserver?

Alors, comment puis-je connaître la direction du défilement lorsque l'événement est déclenché?

Dans l'objet renvoyé, la possibilité la plus proche que je vois est d'interagir avec le type boundingClientRect de sauvegarde de la dernière position de défilement, mais je ne sais pas si la gestion de boundingClientRect aboutira à des problèmes de performances.

Est-il possible d'utiliser l'événement d'intersection pour déterminer la direction de défilement (haut/bas)?

J'ai ajouté cet extrait de base, donc si quelqu'un peut m'aider.
Je serai très reconnaissant.

Voici l'extrait:

var options = {
  rootMargin: '0px',
  threshold: 1.0
}

function callback(entries, observer) { 
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      console.log('entry', entry);
    }
  });
};

var elementToObserve = document.querySelector('#element');
var observer = new IntersectionObserver(callback, options);

observer.observe(elementToObserve);
#element {
  margin: 1500px auto;
  width: 150px;
  height: 150px;
  background: #ccc;
  color: white;
  font-family: sans-serif;
  font-weight: 100;
  font-size: 25px;
  text-align: center;
  line-height: 150px;
}
<div id="element">Observed</div>

J'aimerais le savoir, donc je peux l'appliquer sur le menu des en-têtes fixes pour l'afficher/le cacher

23
Jose Paredes

Je ne sais pas si la gestion de boundingClientRect aboutira à des problèmes de performances.

MDN indique que IntersectionObserver ne s'exécute pas sur le thread principal:

De cette façon, les sites n'ont plus besoin de faire quoi que ce soit sur le thread principal pour surveiller ce type d'intersection d'éléments, et le navigateur est libre d'optimiser la gestion des intersections comme bon lui semble.

MDN, "API Intersection Observer"

Nous pouvons calculer le sens de défilement en enregistrant la valeur de IntersectionObserverEntry.boundingClientRect.y et comparez cela à la valeur précédente.

Exécutez l'extrait de code suivant pour un exemple:

const state = document.querySelector('.observer__state')
const target = document.querySelector('.observer__target')

const thresholdArray = steps => Array(steps + 1)
 .fill(0)
 .map((_, index) => index / steps || 0)

let previousY = 0
let previousRatio = 0

const handleIntersect = entries => {
  entries.forEach(entry => {
    const currentY = entry.boundingClientRect.y
    const currentRatio = entry.intersectionRatio
    const isIntersecting = entry.isIntersecting

    // Scrolling down/up
    if (currentY < previousY) {
      if (currentRatio > previousRatio && isIntersecting) {
        state.textContent ="Scrolling down enter"
      } else {
        state.textContent ="Scrolling down leave"
      }
    } else if (currentY > previousY && isIntersecting) {
      if (currentRatio < previousRatio) {
        state.textContent ="Scrolling up leave"
      } else {
        state.textContent ="Scrolling up enter"
      }
    }

    previousY = currentY
    previousRatio = currentRatio
  })
}

const observer = new IntersectionObserver(handleIntersect, {
  threshold: thresholdArray(20),
})

observer.observe(target)
html,
body {
  margin: 0;
}

.observer__target {
  position: relative;
  width: 100%;
  height: 350px;
  margin: 1500px 0;
  background: rebeccapurple;
}

.observer__state {
  position: fixed;
  top: 1em;
  left: 1em;
  color: #111;
  font: 400 1.125em/1.5 sans-serif;
  background: #fff;
}
<div class="observer__target"></div>
<span class="observer__state"></span>

Si la fonction d'assistance thresholdArray peut vous dérouter, elle crée un tableau allant de 0.0 à 1.0par le nombre d'étapes indiqué. Qui passe 5 renverra [0.0, 0.2, 0.4, 0.6, 0.8, 1.0].

12
Jason

En comparant boundingClientRect et rootBounds de entry, vous pouvez facilement savoir si la cible est au-dessus ou en dessous de la fenêtre.

Pendant callback(), vous vérifiez isAbove/isBelow puis, à la fin, vous le stockez dans wasAbove/wasBelow. La prochaine fois, si la cible apparaît dans la fenêtre (par exemple), vous pouvez vérifier si elle était au-dessus ou en dessous. Vous savez donc si cela vient du haut ou du bas.

Vous pouvez essayer quelque chose comme ceci:

var wasAbove = false;

function callback(entries, observer) {
    entries.forEach(entry => {
        const isAbove = entry.boundingClientRect.y < entry.rootBounds.y;

        if (entry.isIntersecting) {
            if (wasAbove) {
                // Comes from top
            }
        }

        wasAbove = isAbove;
    });
}

J'espère que cela t'aides.

6
thierrymichel

:)

Je ne pense pas que cela soit possible avec une seule valeur de seuil. Vous pouvez essayer de faire attention aux intersectionRatio qui, dans la plupart des cas, se situent en dessous de 1 lorsque le conteneur quitte la fenêtre d'affichage (car l'observateur d'intersection déclenche en mode asynchrone). Je suis sûr que ça pourrait être 1 aussi si le navigateur rattrape assez rapidement. (Je n'ai pas testé cela: D)

Mais ce que vous pourriez peut-être faire, c'est observer deux seuils en utilisant plusieurs valeurs. :)

threshold: [0.9, 1.0]

Si vous obtenez un événement pour le 0.9 d'abord, il est clair que le conteneur entre dans la fenêtre ...

J'espère que cela t'aides. :)

2
stefan judis

Mon exigence était:

  • ne rien faire en défilement
  • en faisant défiler vers le bas, décidez si un élément a commencé à se cacher du haut de l'écran

J'avais besoin de voir quelques informations fournies par IntersectionObserverEntry:

  • intersectionRatio (devrait diminuer de 1.0)
  • boundingClientRect.bottom
  • boundingClientRect.height

Donc, le rappel a fini par ressembler à:

intersectionObserver = new IntersectionObserver(function(entries) {
  const entry = entries[0]; // observe one element
  const currentRatio = intersectionRatio;
  const newRatio = entry.intersectionRatio;
  const boundingClientRect = entry.boundingClientRect;
  const scrollingDown = currentRatio !== undefined && 
    newRatio < currentRatio &&
    boundingClientRect.bottom < boundingClientRect.height;

  intersectionRatio = newRatio;

  if (scrollingDown) {
    // it's scrolling down and observed image started to hide.
    // so do something...
  }

  console.log(entry);
}, { threshold: [0, 0.25, 0.5, 0.75, 1] });

Voir mon message pour les codes complets.

1
bob