Scrollytelling

Funkcje internetowe: Animacje sterowane przewijaniem

Scrollytelling to popularna technika używana do tworzenia angażujących i wciągających treści internetowych. Polega na animowaniu elementów na stronie podczas przewijania, co pozwala na skuteczne opowiedzenie historii lub przeprowadzenie użytkownika przez narrację. Dzięki animacjom CSS opartym na przewijaniu możesz tworzyć te efekty bezpośrednio w CSS, bez konieczności korzystania z JavaScriptu. Animacje są kontrolowane przez pozycję przewijania, a nie przez zegar, co zapewnia ich synchronizację z przewijaniem przez użytkownika.

Jak to zrobić?

Aby utworzyć efekt scrollytellingu, potrzebujesz 2 zestawów elementów: jednego do śledzenia pozycji przewijania i drugiego do animacji.

Najpierw zdefiniuj nazwany view-timeline w elementach, które chcesz śledzić. Będą one służyć jako czynniki sterujące animacjami.

#tracked {
  section:nth-child(1){ view-timeline: --tl-1 block; }
  section:nth-child(2){ view-timeline: --tl-2 block; }
  section:nth-child(3){ view-timeline: --tl-3 block; }
  section:nth-child(4){ view-timeline: --tl-4 block; }
  section:nth-child(5){ view-timeline: --tl-5 block; }
}

Następnie zastosuj animacje do elementów, które chcesz animować, i połącz je z utworzonymi osiami czasu za pomocą właściwości animation-timeline.

#animated {
  section {
    animation: animate-in auto linear both, animate-out auto linear forwards;
    animation-range: entry 25% cover 50%, exit 50% exit 75%;
  }

  section:nth-child(1){ animation-timeline: --tl-1; }
  section:nth-child(2){ animation-timeline: --tl-2; }
  section:nth-child(3){ animation-timeline: --tl-3; }
  section:nth-child(4){ animation-timeline: --tl-4; }
  section:nth-child(5){ animation-timeline: --tl-5; }
}

Aby animation-timeline mogła odwoływać się do nazwanych osi czasu, muszą one znajdować się w tym samym zakresie. Możesz użyć właściwości timeline-scope we wspólnym elemencie nadrzędnym, aby udostępnić osie czasu wszystkim elementom, które ich potrzebują. Często dobrym wyborem jest element :root.

html {
  timeline-scope: --tl-1, --tl-2, --tl-3, --tl-4, --tl-5;
}

Możesz też użyć właściwości animation-range, aby określić dokładny zakres osi czasu, w którym animacja ma być odtwarzana. Dzięki temu masz pełną kontrolę nad tym, kiedy animacje są uruchamiane i jak przebiegają.

#animated section {
  animation-range: entry 25% cover 50%, exit 50% exit 75%;
}

Przykładowy kod

html {
  timeline-scope: --tl-1, --tl-2, --tl-3, --tl-4, --tl-5;
}

#tracked {
  section:nth-child(1){ view-timeline: --tl-1 block; }
  section:nth-child(2){ view-timeline: --tl-2 block; }
  section:nth-child(3){ view-timeline: --tl-3 block; }
  section:nth-child(4){ view-timeline: --tl-4 block; }
  section:nth-child(5){ view-timeline: --tl-5 block; }
}

@keyframes animate-in {
  from { scale: 0.5; opacity: 0; transform: rotateY(-180deg); }
  to { transform: rotateY(0deg); }
}
@keyframes animate-out {
  to { translate: 100% 0; opacity: 0; }
}

#animated {
  section {
    animation: animate-in auto linear both, animate-out auto linear forwards;
    animation-range: entry 25% cover 50%, exit 50% exit 75%;
    backface-visibility: hidden;
  }

  section:nth-child(1){ animation-timeline: --tl-1; }
  section:nth-child(2){ animation-timeline: --tl-2; }
  section:nth-child(3){ animation-timeline: --tl-3; }
  section:nth-child(4){ animation-timeline: --tl-4; }
  section:nth-child(5){ animation-timeline: --tl-5; }
}

/* MANDATORY Copy-Paste Safety: Disable continuous storytelling motion for sensitive users */
@media (prefers-reduced-motion: reduce) {
  #animated section {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}

Sprawdzone metody

Podczas korzystania z animacji sterowanych przewijaniem warto stosować kilka sprawdzonych metod, aby zapewnić płynne i dostępne działanie:

  • DO include feature detection: Not all browsers support scroll-driven animations. Użyj @supports ((animation-timeline: scroll()) and (animation-range: 0% 100%)), aby sprawdzić, czy jest obsługiwany, i zapewnić rozwiązanie zastępcze w przypadku przeglądarek, które go nie obsługują.
    • MUSISZ uwzględnić tutaj sprawdzenie (animation-range: 0% 100%), aby odfiltrować przeglądarki, które obsługują tylko częściowo.
    • NIE używaj pakietu scroll-timeline-polyfill w strategii zastępczej, ponieważ nie jest on kompletny i ma wiele znanych problemów.
    • Jeśli animacja ma charakter wyłącznie dekoracyjny, wybierz progresywne ulepszanie i NIE podawaj kreacji zastępczej.
  • DO respect user preferences: Some users prefer to have less motion on the web. Użyj zapytania o media prefers-reduced-motion, aby wyłączyć lub ograniczyć animacje dla tych użytkowników.
  • 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. Animowanie innych właściwości, takich jak width lub height, może powodować problemy z wydajnością.
  • DO use the correct declaration order: When using the animation shorthand property, declare animation-timeline and animation-range after it to prevent the shorthand from resetting the timeline.

Gdy używasz właściwości view-timeline do tworzenia animacji sterowanej przewijaniem:

  • UŻYWAJ nazwy usługi porównywania cen <dashed-ident>.
  • OPCJONALNIE: określ oś, którą chcesz śledzić. Jeśli nie kierujesz reklam na domyślną oś block (np. w przypadku poziomego paska przewijania), określ, którą oś chcesz śledzić za pomocą view-timeline-axis.
  • ZADBAJ o to, aby zakres wyszukiwania działał: gdy element, który deklaruje view-timeline, nie jest przodkiem elementu animowanego w płaskim drzewie, zwiększ widoczność nazwy view-timeline, używając timeline-scope we wspólnym przodku.

Strategie zastępcze

W przypadku przeglądarek, które nie obsługują animacji sterowanych przewijaniem, możesz użyć kreacji zastępczej, aby odtworzyć efekty wizualne. Kreacje zastępcze są zwykle tworzone za pomocą detektora przewijania (w przypadku efektów ScrollTimeline) lub interfejsu IntersectionObserver API (w przypadku efektów ViewTimeline).

W przeglądarkach z wbudowaną obsługą animacji opartych na przewijaniu ZAWSZE używaj natywnej implementacji CSS, ponieważ jest ona bardziej wydajna.

Pamiętaj, że nie każdy efekt można odtworzyć za pomocą kreacji zastępczych.

W tym konkretnym przypadku poniższy skrypt stosuje kreację zastępczą w przeglądarkach, które nie obsługują animacji sterowanych przewijaniem. Używa interfejsu IntersectionObserver do śledzenia widoczności każdego elementu #tracked section i odpowiednio aktualizuje właściwość transform odpowiedniego elementu #animated section.

const animatedSections = document.querySelectorAll('#animated section');

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    const sectionIndex = Array.from(document.querySelectorAll('#tracked section')).indexOf(entry.target);
    if (sectionIndex !== -1) {
      const animatedSection = animatedSections[sectionIndex];
      const ratio = entry.intersectionRatio;

      // Animate-in
      animatedSection.style.opacity = ratio;
      animatedSection.style.transform = `scale(${0.5 + ratio * 0.5}) rotateY(${-180 + ratio * 180}deg)`;

      // Animate-out
      if (ratio < 0.5) {
        animatedSection.style.translate = `${(0.5 - ratio) * 2 * 100}% 0`;
      } else {
        animatedSection.style.translate = '0 0';
      }
    }
  });
}, { threshold: Array.from({length: 101}, (_, i) => i / 100) });

document.querySelectorAll('#tracked section').forEach(section => {
  observer.observe(section);
});

I odpowiedni kod CSS:

#animated section {
  opacity: 0;
  transform: scale(0.5)  rotateY(-180deg);
  backface-visibility: hidden;
}

/* MANDATORY Copy-Paste Safety: Ensure content remains fully visible and legible for assistive technologies or users with motion sensitivities */
@media (prefers-reduced-motion: reduce) {
  #animated section {
    opacity: 1 !important;
    transform: none !important;
    translate: 0 0 !important;
  }
}

Ta kreacja zastępcza zapewnia dokładniejszą animację sterowaną przewijaniem w przeglądarkach, które nie obsługują natywnej funkcji CSS, co zapewnia bardziej spójne wrażenia wszystkim użytkownikom. Używając serii progów dla elementu IntersectionObserver, możemy dokładniej śledzić pozycję przewijania i tworzyć płynniejsze animacje.