Как создать эффект карусели

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

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

Вот как создать эффекты слайдов карусели:

  1. Создайте прокручиваемый элемент. Он будет служить контейнером для слайдов карусели. В этом примере используется overflow-x: scroll, чтобы разрешить горизонтальную прокрутку.

    <ul class="scroller">
      <li class="entry">1</li>
      <li class="entry">2</li>
      <li class="entry">3</li>
      …
    </ul>
    
    .scroller {
      overflow-x: scroll;
    }
    
  2. Определите анимацию. Создайте CSS-анимацию, которая определяет разные состояния слайдов при их перемещении по области прокрутки. Ключевые кадры можно задавать для любой части анимации. Например, вы можете определить состояние, когда слайды находятся в центре области прокрутки, включив ключевой кадр 50%. В этом примере свойство scale увеличивает слайды по мере их приближения к центру и уменьшает по мере удаления.

    @keyframes animate {
      0% {
        scale: 0.5;
      }
      50% {
        scale: 1;
      }
      100% {
        scale: 0.5;
      }
    }
    
  3. Примените анимацию и view-timeline. Прикрепите анимацию к слайдам карусели и свяжите ее с view-timeline, который отслеживает элемент при прокрутке контейнера.

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

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

Пример кода

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

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

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

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

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

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

  • РЕКОМЕНДУЕТСЯ включить обнаружение функций. Не все браузеры поддерживают анимацию, управляемую прокруткой. Используйте @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 и animation-range после него, чтобы сокращенное свойство не сбрасывало временную шкалу.

Именованные временные шкалы view-timeline лучше использовать, когда нужно анимировать несколько элементов DOM на основе одной временной шкалы или когда нужно анимировать дочерние элементы элемента, для которого определена временная шкала view-timeline. Если анимируемый элемент также определяет view-timeline, вы можете использовать анонимную временную шкалу просмотра с помощью view().

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

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

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

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

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

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

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

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

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

В частности, для этого варианта использования следующий скрипт применяет резервный вариант для браузеров, которые не поддерживают анимацию с прокруткой. Для создания приостановленной анимации для каждого элемента карусели используется Web Animations API (Element.animate()). Затем он прослушивает событие scroll на скроллере и обновляет currentTime каждой анимации на основе прогресса прокрутки элемента в скроллере.

// 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();
}