Karussell-Folieneffekte erstellen

Mit Karussell-Folieneffekten können Sie ein Karussell visuell interessanter gestalten. Wenn der Nutzer durch die Folien scrollt, kann jede Folie animiert werden, wenn sie in den Scrollport eintritt, sich darin befindet und ihn verlässt. Folien können beispielsweise ein- und ausgeblendet, gedreht oder vergrößert/verkleinert werden. Das sorgt für eine dynamische und ansprechende Nutzererfahrung. Im Gegensatz zu einfachen Ein- und Ausblendanimationen wird bei diesem Effekt eine einzige, kontinuierliche Animation verwendet, um die Darstellung der Folie im gesamten Scrollport zu steuern.

Implementierung

So erstellen Sie Karussell-Folieneffekte:

  1. Scroller erstellen: Dieses Element dient als Container für die Karussellfolien. In diesem Beispiel wird overflow-x: scroll verwendet, um horizontales Scrollen zu ermöglichen.

    <ul class="scroller">
      <li class="entry">1</li>
      <li class="entry">2</li>
      <li class="entry">3</li>
      …
    </ul>
    
    .scroller {
      overflow-x: scroll;
    }
    
  2. Animation definieren: Erstellen Sie eine CSS-Animation, die die verschiedenen Zustände Ihrer Folien definiert, während sie den Scrollport durchlaufen. Sie können Keyframes für jeden Teil der Animation definieren. Sie können beispielsweise einen Status für den Fall definieren, dass sich die Folien in der Mitte des Scrollports befinden, indem Sie einen 50%-Keyframe einfügen. In diesem Beispiel sorgt das Attribut scale dafür, dass die Folien größer werden, wenn sie sich der Mitte nähern, und kleiner, wenn sie sich von ihr entfernen.

    @keyframes animate {
      0% {
        scale: 0.5;
      }
      50% {
        scale: 1;
      }
      100% {
        scale: 0.5;
      }
    }
    
  3. Animation und view-timeline anwenden: Hängen Sie die Animation an die Karussellfolien an und verknüpfen Sie sie mit einem view-timeline, das das Element beim Scrollen durch den Container verfolgt.

    .scroller > * {
      animation: animate auto linear both;
      animation-timeline: view(inline);
    }
    

    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.

Beispielcode

Mit diesem Code werden die Karussellelemente eines horizontalen Scrollers beim Scrollen mithilfe einer anonymen Ansichtszeitachse animiert:

@keyframes animate {
  0% {
    scale: 0.5;
  }

  50% {
    scale: 1;
  }

  100% {
    scale: 0.5;
  }
}

.scroller > * {
  /* Applies the animation using an `auto` duration */
  animation: animate auto linear both;
  /* Sets the animation timeline to use an anonymous view progress timeline, tracking the element's progress through the scroller on the inline axis */
  animation-timeline: view(inline);
}

Mit diesem Code werden die Karussellelemente eines horizontalen Scrollers beim Scrollen mithilfe einer benannten Ansichtszeitachse animiert:

@keyframes animate {
  0% {
    scale: 0.5;
  }

  50% {
    scale: 1;
  }

  100% {
    scale: 0.5;
  }
}

/* This creates a named view-timeline on each carousel item. The timeline is used to drive the animation that is applied on the same element. */
.scroller > * {
  /* Applies the animation using an `auto` duration */
  animation: animate auto linear both;
  /* Defines a named view progress timeline, tracking the element's progress through the scroller on the inline axis */
  view-timeline: --item inline;
  /* Sets the animation timeline to use the named view progress timeline defined above */
  animation-timeline: --item;
}

Best Practices

Wenn Sie scrollbasierte 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 NICHT das Paket scroll-timeline-polyfill für die Fallback-Strategie, 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 und animation-range danach, um zu verhindern, dass die Kurzschreibweise die Zeitachse zurücksetzt.

Verwenden Sie eine benannte view-timeline, wenn mehrere DOM-Elemente basierend auf derselben Zeitachse animiert werden sollen oder wenn Sie untergeordnete Elemente des Elements animieren möchten, für das die view-timeline definiert ist. Wenn das Element, das Sie animieren, auch das Element ist, das die view-timeline definiert, können Sie eine anonyme View-Timeline mit view() verwenden.

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 Sie die Eigenschaft view-timeline verwenden, um eine scrollbasierte Animation zu erstellen:

  • DO use a CSS <dashed-ident> for the name (e.g. view-timeline: --my-custom-name)
  • 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.

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. Dabei wird die Web Animations API (Element.animate()) verwendet, um für jedes Element im Karussell eine pausierte Animation zu erstellen. Anschließend wird auf das scroll-Ereignis im Scroller gewartet und die currentTime jeder Animation wird basierend auf dem Scrollfortschritt des Elements im Scroller aktualisiert.

// Fallback for browsers that don't support scroll-driven animations
if (!CSS.supports('(animation-timeline: view()) and (animation-range: entry)')) {
  const scroller = document.querySelector('.scroller');
  const entries = document.querySelectorAll('.entry');

  // Create a map to store animations
  const animations = new Map();

  entries.forEach(entry => {
    const animation = entry.animate(
      {
        scale: ['0.5', '1', '0.5']
      },
      {
        duration: 1, // We'll control the time ourselves
        fill: 'both'
      }
    );
    animation.pause();
    animations.set(entry, animation);
  });

  // Update animations on scroll
  const tick = () => {
    const scrollerRect = scroller.getBoundingClientRect();

    entries.forEach(entry => {
      const animation = animations.get(entry);
      if (!animation) return;

      const entryRect = entry.getBoundingClientRect();
      const progress = (entryRect.left + entryRect.width / 2 - scrollerRect.left) / scrollerRect.width;

      animation.currentTime = progress;
    });
  };
    
  scroller.addEventListener('scroll', tick);
  tick();
}