Добавьте эффекты входа и выхода для элементов, когда они входят в область прокрутки или выходят из нее.

Эффекты появления и исчезновения – это анимация, которая запускается, когда элемент появляется в области просмотра или исчезает из нее. Это позволяет создавать привлекательные и динамичные интерфейсы. Например, можно использовать эффект появления, чтобы элемент плавно появлялся при прокрутке, или эффект исчезновения, чтобы он уменьшался при прокрутке.

Рекомендации по внедрению

Чтобы добавить эффекты появления и исчезновения элемента, нужно объединить несколько свойств CSS. Ниже приведены пошаговые инструкции.

  1. Создайте отдельные @keyframes для анимации появления и исчезновения. Анимация появления будет применяться, когда элемент входит в область просмотра, а анимация исчезновения – когда он ее покидает.

    @keyframes slide-in {
      from { transform: translateX(-100%); }
    }
    @keyframes slide-out {
      to { transform: translateX(100%); }
    }
    
  2. Прикрепите к элементу ключевые кадры входа и выхода. Для этого нужно определить несколько анимаций в свойстве animation.

    • Задайте для свойства animation-fill-mode значение backwards, чтобы начальное состояние применялось до начала анимации.
    • Задайте для свойства animation-fill-mode значение forwards, чтобы анимация сохраняла свое конечное состояние после завершения.
    .animated-element {
      animation:
        slide-in 1s linear backwards,
        slide-out 1s linear forwards;
    }
    
  3. Создайте временную шкалу просмотра и свяжите ее с анимацией. Временная шкала просмотра – это тип временной шкалы, связанный с видимостью элемента в области просмотра. Вы можете создать функцию плавности с помощью функции view(), а затем применить ее к анимации, используя свойство animation-timeline.

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

    По умолчанию view() отслеживает элемент по оси block. Если вам нужно отслеживать его по оси inline, используйте view(inline).

  4. Ограничьте анимацию диапазонами entry и exit. Свойство animation-range позволяет указать, в какой части временной шкалы должна воспроизводиться анимация.

    • Диапазон entry охватывает время с момента, когда элемент впервые появляется в области просмотра, до момента, когда он становится полностью видимым.
    • Диапазон exit охватывает время с момента, когда элемент начинает покидать область просмотра, до момента, когда он полностью скрывается.
    .animated-element {
      animation-range: entry, exit;
    }
    

Пример кода

Этот код анимирует дочерние элементы прокручиваемого элемента при прокрутке с помощью анонимной временной шкалы просмотра:

@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;
    }
  }
}

Когда элементы входят в область прокрутки, воспроизводится анимация grow, а когда выходят – shrink.

Следующий код имеет тот же визуальный результат, но анимирует прямых дочерних элементов скроллера при прокрутке с помощью именованной временной шкалы просмотра:

@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;
    }
  }
}

Рекомендации

При использовании анимации, управляемой прокруткой, важно следовать нескольким рекомендациям, чтобы обеспечить плавность и доступность:

  • РЕКОМЕНДУЕТСЯ включить обнаружение функций. Не все браузеры поддерживают анимацию, управляемую прокруткой. Используйте @supports ((animation-timeline: view()) and (animation-range: entry)), чтобы проверить поддержку и предоставить резервный вариант для браузеров, которые ее не поддерживают.
    • Проверка (animation-range: entry) ОБЯЗАТЕЛЬНО должна быть включена, чтобы отфильтровать браузеры с частичной поддержкой.
    • НЕ ИСПОЛЬЗУЙТЕ пакет scroll-timeline-polyfill для резервной стратегии, так как он неполный и имеет много известных проблем.
    • Если анимация используется только в декоративных целях, выберите прогрессивное улучшение и НЕ добавляйте резервный вариант.
  • Уважайте предпочтения пользователей. Некоторые пользователи предпочитают, чтобы на веб-страницах было меньше движения. Используйте медиазапрос prefers-reduced-motion, чтобы отключить или уменьшить анимацию для этих пользователей.
  • РЕКОМЕНДУЕТСЯ анимировать только эффективные свойства CSS. Чтобы анимация была максимально плавной, анимируйте свойства, которые могут обрабатываться потоком компоновщика браузера, например transform и opacity. Анимация других свойств, таких как width или height, может привести к проблемам производительности.
  • РЕКОМЕНДУЕТСЯ использовать правильный порядок объявления. При использовании сокращенного свойства animation объявляйте animation-timeline после него, чтобы сокращенное свойство не сбрасывало временную шкалу.

При использовании функции view() для создания анимации, управляемой прокруткой:

  • НЕОБЯЗАТЕЛЬНО. Укажите ось, которую нужно отслеживать. Если таргетинг настроен не на ось block по умолчанию (например, при горизонтальной прокрутке), укажите, какую ось нужно отслеживать, с помощью view(block) или view(inline).
  • Если анимация не применяется к самому отслеживаемому объекту, используйте именованную временную шкалу просмотра.

При использовании свойства view-timeline для создания анимации, управляемой прокруткой:

  • РЕКОМЕНДУЕТСЯ использовать для названия CSS-класс <dashed-ident>.
  • НЕОБЯЗАТЕЛЬНО. Укажите ось, которую нужно отслеживать. Если таргетинг настроен не на ось block по умолчанию (например, при горизонтальной прокрутке), укажите, какую ось нужно отслеживать с помощью view-timeline-axis.
  • РЕКОМЕНДУЕТСЯ убедиться, что область поиска работает. Если элемент, объявляющий view-timeline, не является предком анимированного элемента в плоском дереве, сделайте имя view-timeline видимым, используя timeline-scope для общего предка.

Если нужно анимировать несколько элементов или дочерних элементов отслеживаемого объекта, лучше использовать именованный view-timeline.

Поддержка браузеров и резервные стратегии

Поэтому обычно требуется резервная стратегия.

Для браузеров, которые не поддерживают анимацию на основе прокрутки, можно использовать резервный вариант, чтобы воссоздать визуальные эффекты. Резервные варианты обычно создаются с помощью прослушивателя прокрутки (для эффектов ScrollTimeline) или IntersectionObserver API (для эффектов ViewTimeline).

В браузерах со встроенной поддержкой анимации на основе прокрутки ВСЕГДА используйте нативную реализацию CSS, так как она более эффективна.

Обратите внимание, что не все эффекты можно воссоздать с помощью резервных объявлений.

В частности, для этого варианта использования следующий скрипт применяет резервный вариант для браузеров, которые не поддерживают анимацию с прокруткой. Он использует IntersectionObserver для отслеживания видимости элемента .wrapper и обновляет свойство transform слоев в зависимости от положения прокрутки.

<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>