Zmniejszanie nagłówka podczas przewijania

Funkcje internetowe: Animacje sterowane przewijaniem

Zmniejszający się nagłówek podczas przewijania to powszechny wzorzec interfejsu, w którym stały element nagłówka u góry strony płynnie zmniejsza się, gdy użytkownik przewija stronę w dół. Ten efekt jest często używany do maksymalizacji miejsca na ekranie dla głównych treści przy jednoczesnym zachowaniu dostępu do podstawowych elementów nawigacji lub marki. Dzięki animacjom CSS opartym na przewijaniu można osiągnąć ten efekt w sposób deklaratywny i wydajny, łącząc animację z pozycją przewijania dokumentu.

Jak to zrobić?

Aby utworzyć nagłówek, który zmniejsza się podczas przewijania:

  1. Utwórz stały nagłówek: zacznij od elementu nagłówka, który jest przymocowany u góry strony i ma wstępnie zdefiniowaną wysokość.

    <header>HEADER</header>
    
    header {
      position: fixed;
      height: 200px;
      top: 0;
      left: 0;
      right: 0;
    }
    
  2. Zdefiniuj animację zmniejszania: utwórz animację CSS, która zmienia wysokość nagłówka.

    @keyframes shrink {
      to {
        height: 50px;
      }
    }
    
  3. Zastosowanie animacji i osi czasu przewijania: dołącz animację do nagłówka i użyj funkcji scroll(), aby połączyć ją z pozycją przewijania dokumentu.

    header {
      animation: shrink auto linear both;
      animation-timeline: scroll(block root);
    }
    
  4. Ustaw właściwość animation-range: użyj właściwości animation-range, aby określić odległość przewijania, na której powinna wystąpić animacja. Na przykład, aby zmniejszyć nagłówek po przewinięciu pierwszych 150 pikseli, użyj animation-range: 0px 150px;.

    header {
      animation-range: 0px 150px;
    }
    

Wskazówka: aby zapobiec zasłanianiu treści pod nagłówkiem, dodaj do elementu body (lub kontenera treści głównej) właściwość padding-top o wartości równej początkowej wysokości nagłówka.

Wskazówka: aby zawartość strony przewijała się synchronicznie ze zmniejszającym się nagłówkiem, ustaw animation-range-end na różnicę między rozmiarem początkowym i końcowym. Dzięki temu animacja zakończy się dokładnie wtedy, gdy nagłówek osiągnie ostateczny rozmiar. W tym przykładzie nagłówek zmniejsza się z 200px do 50px, więc właściwość animation-range-end jest ustawiona na 150px.

Przykładowy kod

@keyframes shrink {
  to {
    height: 50px;
  }
}

header {
  animation: shrink auto linear both;
  animation-timeline: scroll(block root);
  animation-range: 0px 150px;
}

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 funkcji scroll() do tworzenia animacji sterowanej przewijaniem:

  • OPCJONALNIE Określ element przewijany: jeśli nie kierujesz na najbliższy element przewijany, określ, którego elementu przewijanego chcesz użyć, za pomocą scroll(root) lub scroll(self).
    • Jeśli root, nearest lub self nie wystarczą, użyj nazwanej osi czasu przewijania.
  • 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 oś, którą chcesz śledzić, za pomocą scroll(block) lub scroll(inline).

W tym konkretnym przypadku użycia:

  • Element, który animujesz, NIE MOŻE być elementem position: static ani position: relative, gdy używasz wartości procentowych w animation-range.
    • Dzieje się tak, ponieważ te elementy są uważane za „w przepływie”. Zmniejszanie tych elementów podczas przewijania zmniejszałoby całkowitą odległość przewijania, co wpływałoby na obliczoną wartość, na przykład 10%, podczas przewijania.

Obsługa przeglądarek i strategie zastępcze

Dlatego zwykle wymagana jest strategia zastępcza.

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 detektora przewijania do śledzenia pozycji przewijania dokumentu na odległość 150px i odpowiednio aktualizuje wysokość nagłówka.

// Fallback for browsers that don't support scroll-driven animations
if (!CSS.supports('(animation-timeline: scroll()) and (animation-range: 0% 100%)')) {
  const header = document.querySelector('header');

  const initialHeight = 200;
  const finalHeight = 50;
  const scrollDistance = 150;

  window.addEventListener('scroll', () => {
    const scrollY = window.scrollY;
    const scrollPercent = Math.min(1, scrollY / scrollDistance);
    const newHeight = initialHeight - (initialHeight - finalHeight) * scrollPercent;

    header.style.height = `${newHeight}px`;
  });
}