Wie man eine scroll-ausgelöste Animation mit grundlegendem JavaScript erstellt

Avatar of Md Shuvo
Md Shuvo am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Ein wenig Animation auf einer Website kann für etwas Flair sorgen, Benutzer beeindrucken und ihre Aufmerksamkeit erregen. Man könnte sie sofort beim Laden der Seite ausführen, egal wo sie sich auf der Seite befinden. Aber was ist, wenn Ihre Website ziemlich lang ist, sodass der Benutzer einige Zeit braucht, um zu diesem Element zu scrollen? Sie könnten es verpassen.

Man könnte sie die ganze Zeit laufen lassen, aber vielleicht ist die Animation am besten so gestaltet, dass man den Anfang davon sicher sieht. Der Trick ist, die Animation zu starten, wenn der Benutzer zu diesem Element herunterscrollt – eine scroll-ausgelöste Animation, wenn man so will.

Um dies anzugehen, verwenden wir Scroll-Trigger. Wenn der Benutzer zu einem bestimmten Element herunterscrollt, können wir dieses Ereignis nutzen, um etwas zu tun. Es könnte alles sein, sogar der Beginn einer Animation. Es könnte sogar scroll-ausgelöstes Lazy Loading für Bilder oder Lazy Loading eines ganzen Kommentarbereichs sein. Auf diese Weise zwingen wir Benutzer nicht, Elemente herunterzuladen, die beim anfänglichen Seitenaufruf nicht im Viewport sind. Viele Benutzer scrollen vielleicht gar nicht nach unten, sodass wir ihnen (und uns) Bandbreite und Ladezeit wirklich sparen.

Scroll-Trigger sind sehr nützlich. Es gibt viele Bibliotheken, die Sie zur Implementierung verwenden können, wie z. B. Greensocks beliebtes ScrollTrigger-Plugin. Aber Sie müssen keine Drittanbieter-Bibliothek verwenden, insbesondere nicht für ziemlich einfache Ideen. Tatsächlich können Sie es selbst mit nur einer Handvoll Vanilla JavaScript implementieren. Das werden wir in diesem Artikel tun.

So erstellen wir unser scroll-ausgelöstes Ereignis

  • Erstellen Sie eine Funktion namens scrollTrigger, die wir auf bestimmte Elemente anwenden können
  • Wenden Sie eine .active-Klasse auf ein Element an, wenn es in den Viewport gelangt
  • Animieren Sie diese .active-Klasse mit CSS

Es gibt Zeiten, in denen das Hinzufügen einer .active-Klasse nicht ausreicht. Zum Beispiel möchten wir vielleicht stattdessen eine benutzerdefinierte Funktion ausführen. Das bedeutet, wir sollten eine benutzerdefinierte Funktion übergeben können, die ausgeführt wird, wenn das Element sichtbar ist. So

scrollTrigger('.loader', {
  cb: function(el) {
    el.innerText = 'Loading ...'
    loadContent()
  }
})

Wir werden auch versuchen, Scroll-Trigger für ältere Browser zu unterstützen, die sie nicht unterstützen.

Aber zuerst die IntersectionObserver API

Das wichtigste JavaScript-Feature, das wir verwenden werden, ist der Intersection Observer. Diese API bietet eine Möglichkeit, asynchron Änderungen an der Schnittmenge eines Zielelements zu beobachten – und das auf eine performantere Weise als das Beobachten von scroll-Ereignissen. Wir werden IntersectionObserver verwenden, um zu überwachen, wann das Scrollen den Punkt erreicht, an dem bestimmte Elemente auf der Seite sichtbar sind.

Lasst uns mit dem Erstellen des Scroll-Triggers beginnen

Wir möchten eine Funktion namens scrollTrigger erstellen, und diese Funktion sollte einen Selektor als Argument entgegennehmen.

function scrollTrigger(selector) {
  // Multiple element can have same class/selector,
  // so we are using querySelectorAll
  let els = document.querySelectorAll(selector)
  // The above `querySelectorAll` returns a nodeList,
  // so we are converting it to an array
  els = Array.from(els)
  // Now we are iterating over the elements array
  els.forEach(el => {
    // `addObserver function` will attach the IntersectionObserver to the element
    // We will create this function next
    addObserver(el)
  })
}
// Example usage
scrollTrigger('.scroll-reveal')

Erstellen wir nun die Funktion addObserver, die wir mit IntersectionObserver an das Element anhängen möchten

function scrollTrigger(selector){
  let els = document.querySelectorAll(selector)
  els = Array.from(els)
  els.forEach(el => {
    addObserver(el)
  })
}
function addObserver(el){
    // We are creating a new IntersectionObserver instance
    let observer = new IntersectionObserver((entries, observer) => { // This takes a callback function that receives two arguments: the elements list and the observer instance.
      entries.forEach(entry => {
        // `entry.isIntersecting` will be true if the element is visible
      if(entry.isIntersecting) {
        entry.target.classList.add('active')
        // We are removing the observer from the element after adding the active class
        observer.unobserve(entry.target)
      }
    })
  })
  // Adding the observer to the element
  observer.observe(el)
}
// Example usage
scrollTrigger('.scroll-reveal')

Wenn wir dies tun und zu einem Element mit der Klasse .scroll-reveal scrollen, wird eine .active-Klasse zu diesem Element hinzugefügt. Beachten Sie jedoch, dass die active-Klasse hinzugefügt wird, sobald ein kleiner Teil des Elements sichtbar ist.

Scroll-ausgelöste Animation mit dem Code dafür links und geöffneten DevTools rechts.

Aber das könnte übertrieben sein. Stattdessen möchten wir vielleicht, dass die .active-Klasse hinzugefügt wird, sobald ein *größerer* Teil des Elements sichtbar ist. Glücklicherweise akzeptiert IntersectionObserver einige Optionen dafür als zweites Argument. Wenden wir diese auf unsere scrollTrigger-Funktion an

// Receiving options as an object
// If the user doesn't pass any options, the default will be `{}`
function scrollTrigger(selector, options = {}) {
  let els = document.querySelectorAll(selector)
  els = Array.from(els)
  els.forEach(el => {
    // Passing the options object to the addObserver function
    addObserver(el, options)
  })
}
// Receiving options passed from the scrollTrigger function
function addObserver(el, options) {
  let observer = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
      if(entry.isIntersecting) {
        entry.target.classList.add('active')
        observer.unobserve(entry.target)
      }
    })
  }, options) // Passing the options object to the observer
  observer.observe(el)
}
// Example usage 1:
// scrollTrigger('.scroll-reveal')
// Example usage 2:
scrollTrigger('.scroll-reveal', {
  rootMargin: '-200px'
})

Und schon sind unsere ersten beiden Tagesordnungspunkte erfüllt!

Gehen wir zum dritten Punkt über – dem Hinzufügen der Möglichkeit, eine Callback-Funktion auszuführen, wenn wir zu einem Ziel-Element scrollen. Insbesondere übergeben wir die Callback-Funktion in unserem Options-Objekt als cb

function scrollTrigger(selector, options = {}) {
  let els = document.querySelectorAll(selector)
  els = Array.from(els)
  els.forEach(el => {
    addObserver(el, options)
  })
}
function addObserver(el, options){
  let observer = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
      if(entry.isIntersecting){
        if(options.cb) {
          // If we've passed a callback function, we'll call it
          options.cb(el)
        } else{
          // If we haven't, we'll just add the active class
          entry.target.classList.add('active')
        }
        observer.unobserve(entry.target)
      }
    })
  }, options)
  observer.observe(el)
}
// Example usage:
scrollTrigger('.loader', {
  rootMargin: '-200px',
  cb: function(el){
    el.innerText = 'Loading...'
    // Done loading
    setTimeout(() => {
      el.innerText = 'Task Complete!'
    }, 1000)
  }
})
An updated animated screenshot of the same scroll-triggered animation. As boxes enter the screen from the bottom, a they rotate. A "loading" message that changes to "finished loading" message is the last element to scroll into view. The code is open to the left of the animation.

Großartig! Es gibt noch eine letzte Sache, um die wir uns kümmern müssen: die Unterstützung älterer Browser. Bestimmte Browser unterstützen möglicherweise IntersectionObserver nicht. Lassen Sie uns diesen Fall also in unserer addObserver-Funktion behandeln

function scrollTrigger(selector, options = {}) {
  let els = document.querySelectorAll(selector)
  els = Array.from(els)
  els.forEach(el => {
    addObserver(el, options)
  })
}
function addObserver(el, options) {
  // Check if `IntersectionObserver` is supported
  if(!('IntersectionObserver' in window)) {
    // Simple fallback
    // The animation/callback will be called immediately so
    // the scroll animation doesn't happen on unsupported browsers
    if(options.cb){
      options.cb(el)
    } else{
      entry.target.classList.add('active')
    }
    // We don't need to execute the rest of the code
    return
  }
  let observer = new IntersectionObserver((entries, observer) =>; {
    entries.forEach(entry => {
      if(entry.isIntersecting) {
        if(options.cb) {
          options.cb(el)
        } else{
          entry.target.classList.add('active')
        }
        observer.unobserve(entry.target)
      }
    })
  }, options)
  observer.observe(el)
}
// Example usages:
scrollTrigger('.intro-text')
scrollTrigger('.scroll-reveal', {
  rootMargin: '-200px',
})
scrollTrigger('.loader', {
  rootMargin: '-200px',
  cb: function(el){
    el.innerText = 'Loading...'
    setTimeout(() => {
      el.innerText = 'Task Complete!'
    }, 1000)
  }
})

Hier ist noch einmal die Live-Demo

Das war alles für diese kleine Reise! Ich hoffe, Sie haben sie genossen und dabei etwas Neues gelernt.