Eingangs- und Ausgangseffekte für Elemente hinzufügen, wenn sie den Scrollport betreten oder verlassen

Eingangs- und Ausgangseffekte sind Animationen, die ausgelöst werden, wenn ein Element in den Darstellungsbereich eintritt oder ihn verlässt. Damit lassen sich ansprechende und dynamische Nutzererlebnisse schaffen. Sie können beispielsweise einen Eingangseffekt verwenden, um ein Element einzublenden, wenn es in den sichtbaren Bereich gescrollt wird, oder einen Ausgangseffekt, um es zu verkleinern, wenn es aus dem sichtbaren Bereich gescrollt wird.

Implementierung

Wenn Sie einem Element Ein- und Ausstiegseffekte hinzufügen möchten, müssen Sie mehrere CSS-Eigenschaften kombinieren. Im Folgenden erhalten Sie eine ausführliche Anleitung:

  1. Erstellen Sie separate @keyframes für die Ein- und Ausblendanimationen. Die Eingangsanimation wird angewendet, wenn das Element in den Darstellungsbereich eingeblendet wird, und die Ausgangsanimation, wenn es ausgeblendet wird.

    @keyframes slide-in {
      from { transform: translateX(-100%); }
    }
    @keyframes slide-out {
      to { transform: translateX(100%); }
    }
    
  2. Hängen Sie die Keyframes für den Ein- und Ausstieg an das Element an. Dazu definieren Sie mehrere Animationen im Attribut animation.

    • Geben Sie der Eingangsanimation einen animation-fill-mode-Wert von backwards, damit der Anfangszustand vor dem Start der Animation angewendet wird.
    • Geben Sie der Ausgangsanimation einen animation-fill-mode von forwards, damit sie nach Abschluss der Animation ihren Endzustand beibehält.
    .animated-element {
      animation:
        slide-in 1s linear backwards,
        slide-out 1s linear forwards;
    }
    
  3. Erstellen Sie eine ViewTimeline und verknüpfen Sie sie mit den Animationen. Eine View Timeline ist eine Art von Zeitachse, die mit der Sichtbarkeit eines Elements im Darstellungsbereich verknüpft ist. Sie können eine mit der Funktion view() erstellen und sie dann mit der Property animation-timeline auf Ihre Animationen anwenden.

    .animated-element {
      animation-timeline: view();
    }
    

    Standardmäßig verfolgt view() das Element auf der block-Achse. Wenn Sie die Bewegung auf der inline-Achse verfolgen müssen, können Sie view(inline) verwenden.

  4. Beschränken Sie die Animationen auf die Bereiche entry und exit. Mit der Property animation-range können Sie angeben, in welchem Teil der Zeitachse eine Animation ausgeführt werden soll.

    • Der Bereich entry deckt die Zeit ab, die vergeht, bis das Element vollständig sichtbar ist, nachdem es zum ersten Mal im Darstellungsbereich angezeigt wurde.
    • Der Bereich exit deckt die Zeit ab, in der das Element den Darstellungsbereich verlässt, bis es vollständig ausgeblendet ist.
    .animated-element {
      animation-range: entry, exit;
    }
    

Beispielcode

Mit diesem Code werden die direkten untergeordneten Elemente des Scrollers beim Scrollen mithilfe einer anonymen Ansichtszeitachse animiert:

@media (prefers-reduced-motion: no-preference) {
  @supports ((animation-timeline: view()) and (animation-range: entry)) {
    @keyframes grow {
      from {
        scale: 0.5;
      }
    }
    @keyframes shrink {
      to {
        scale: 0.5;
      }
    }

    .scroller > * {
      animation:
        grow auto linear backwards,
        shrink auto linear forwards;
      animation-timeline: view(inline);
      animation-range: entry, exit;
    }
  }
}

Wenn die Elemente in den Scrollport gelangen, wird die Animation grow abgespielt. Wenn sie den Scrollport verlassen, wird die Animation shrink abgespielt.

Der folgende Code hat dasselbe visuelle Ergebnis, animiert aber die direkten untergeordneten Elemente des Scrollers beim Scrollen mit einer benannten view-timeline:

@media (prefers-reduced-motion: no-preference) {
  @supports ((animation-timeline: view()) and (animation-range: entry)) {
    @keyframes grow {
      from {
        scale: 0.5;
      }
    }
    @keyframes shrink {
      to {
        scale: 0.5;
      }
    }

    .scroller > * {
      view-timeline: --tl inline;
      animation:
        grow auto linear backwards,
        shrink auto linear forwards;
      animation-timeline: --tl;
      animation-range: entry, exit;
    }
  }
}

Best Practices

Wenn Sie scrollgesteuerte Animationen verwenden, sollten Sie einige Best Practices beachten, um eine reibungslose und barrierefreie Nutzung zu ermöglichen:

  • DO include feature detection: Not all browsers support scroll-driven animations. Verwenden Sie @supports ((animation-timeline: view()) and (animation-range: entry)), um die Unterstützung zu prüfen, und stellen Sie einen Fallback für Browser bereit, die die Funktion nicht unterstützen.
    • Die (animation-range: entry)-Prüfung MUSS hier enthalten sein, um Browser mit nur teilweiser Unterstützung herauszufiltern.
    • Verwenden Sie für die Fallback-Strategie NICHT das Paket scroll-timeline-polyfill, da es nicht vollständig ist und viele bekannte Probleme aufweist.
    • Wenn die Animation nur dekorativ ist, entscheide dich für die progressive Verbesserung und stelle KEIN Fallback bereit.
  • DO respect user preferences: Some users prefer to have less motion on the web. Verwenden Sie die Medienabfrage prefers-reduced-motion, um Ihre Animationen für diese Nutzer zu deaktivieren oder zu reduzieren.
  • DO try to animate only performant CSS properties: For the smoothest animations, stick to animating properties that can be handled by the browser's compositor thread, such as transform and opacity. Das Animieren anderer Attribute wie width oder height kann zu Leistungsproblemen führen.
  • RICHTIG: Verwenden Sie die richtige Deklarationsreihenfolge. Wenn Sie die Kurzschreibweise animation verwenden, deklarieren Sie animation-timeline danach, um zu verhindern, dass die Kurzschreibweise die Zeitachse zurücksetzt.

Wenn Sie die Funktion view() verwenden, um eine scrollbasierte Animation zu erstellen, gilt Folgendes:

  • OPTIONAL: Geben Sie die zu verfolgende Achse explizit an. Wenn Sie nicht die Standardachse block verwenden (z. B. bei einem horizontalen Scroller), geben Sie mit view(block) oder view(inline) explizit an, welche Achse verfolgt werden soll.
  • Wenn die Animation nicht auf das verfolgte Subjekt selbst angewendet wird, verwenden Sie eine benannte Ansichtszeitachse.

Wenn Sie die Eigenschaft view-timeline verwenden, um eine scrollbasierte Animation zu erstellen:

  • Verwenden Sie für den Namen ein CSS-<dashed-ident>. (EMPFOHLEN)
  • OPTIONAL: Achte darauf, die zu verfolgende Achse explizit anzugeben. Wenn du nicht die Standardachse block verwendest (z. B. bei einem horizontalen Scroller), gib explizit an, welche Achse mit view-timeline-axis verfolgt werden soll.
  • DO: Achten Sie darauf, dass der Suchbereich funktioniert. Wenn das Element, das die view-timeline deklariert, kein flacher Baum-Ancestor des animierten Elements ist, erhöhen Sie die Sichtbarkeit des Namens der view-timeline, indem Sie timeline-scope für einen gemeinsamen Ancestor verwenden.

Bevorzugen Sie ein benanntes view-timeline, wenn mehrere Elemente oder untergeordnete Elemente des verfolgten Objekts animiert werden müssen.

Browserunterstützung und Fallback-Strategien

Daher ist in der Regel eine Fallback-Strategie erforderlich.

Für Browser, die keine scrollgesteuerten Animationen unterstützen, können Sie einen Fallback verwenden, um die visuellen Effekte nachzubilden. Die Fallbacks werden in der Regel entweder mit einem Scroll-Listener (für ScrollTimeline-Effekte) oder der IntersectionObserver API (für ViewTimeline-Effekte) erstellt.

Verwenden Sie in Browsern mit integrierter Unterstützung für scrollgesteuerte Animationen IMMER die native CSS-Implementierung, da diese leistungsfähiger ist.

Beachten Sie, dass nicht jeder Effekt mit dem Fallback-Ansatz nachgebildet werden kann.

Speziell für diesen Anwendungsfall wird im folgenden Script der Fallback für Browser angewendet, die keine scrollgesteuerten Animationen unterstützen. Es wird ein IntersectionObserver verwendet, um die Sichtbarkeit des .wrapper-Elements zu verfolgen, und die transform-Eigenschaft der Ebenen wird basierend auf der Scrollposition aktualisiert.

<script>
  if (!CSS.supports('(animation-timeline: view()) and (animation-range: entry)')) {
    const observer = new IntersectionObserver(
      (entries) => {
        for (const entry of entries) {
          // This matches the effect as defined in the CSS example above.
          // Customize this further if needed.
          entry.target.style.scale = 0.5 + entry.intersectionRatio * 0.5;
        }
      },
      {
        threshold: Array.from({ length: 101 }, (_, i) => i / 100),
      }
    );

    document.querySelectorAll('.scroller > *').forEach((el) => {
      observer.observe(el);
    });
  }
</script>