Scrollytelling

Скроллителлинг – это популярный метод создания увлекательного и захватывающего веб-контента. Она заключается в том, что элементы на странице анимируются по мере прокрутки, рассказывая историю или направляя пользователя по повествованию. С помощью CSS Scroll-Driven Animations вы можете создавать такие эффекты непосредственно в CSS, не полагаясь на JavaScript. Анимация управляется положением прокрутки, а не часами, что обеспечивает ее синхронизацию с прокруткой пользователя.

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

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

Сначала определите именованный view-timeline для элементов, которые нужно отслеживать. Они будут управлять анимацией.

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

Затем примените анимацию к нужным элементам и свяжите их с созданными временными шкалами, используя свойство 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; }
}

Чтобы animation-timeline мог ссылаться на именованные временные шкалы, они должны находиться в одной области видимости. Вы можете использовать свойство timeline-scope для общего предка, чтобы сделать временные шкалы доступными для всех элементов, которым они нужны. Для этого часто подходит элемент :root.

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

Наконец, с помощью свойства animation-range можно указать точный диапазон временной шкалы, в течение которого должна воспроизводиться анимация. Это позволяет точно контролировать, когда запускается анимация и как она выполняется.

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

Пример кода

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

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

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

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

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

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

Резервные стратегии

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

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

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

В частности, для этого варианта использования следующий скрипт применяет резервный вариант для браузеров, которые не поддерживают анимацию с прокруткой. Он использует IntersectionObserver, чтобы отслеживать видимость каждого элемента #tracked section и соответствующим образом обновлять свойство transform соответствующего элемента #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);
});

и соответствующий код 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;
  }
}

Это резервное решение обеспечивает более точную анимацию, управляемую прокруткой, для браузеров, которые не поддерживают нативную функцию CSS, обеспечивая более согласованную работу для всех пользователей. Используя ряд пороговых значений для IntersectionObserver, мы можем точнее отслеживать положение прокрутки и создавать более плавную анимацию.