스크롤 진행률 표시기 빌드

스크롤 진행률 표시기는 스크롤 가능한 문서 또는 컨테이너를 통한 사용자의 진행 상황을 시각적으로 전달하는 일반적인 사용자 인터페이스 패턴입니다. 사용자가 스크롤하면 시각적 요소가 업데이트되어 위치를 반영하므로 얼마나 많은 콘텐츠를 보았고 얼마나 남았는지 명확하고 직관적으로 알 수 있습니다.

구현 방법

스크롤 진행률 표시기를 만들려면 다음 두 가지가 필요합니다.

  1. 진행률 표시줄 역할을 하는 요소입니다. 이 요소는 일반적으로 position: fixed 또는 position: absolute이므로 사용자가 스크롤하는 동안 계속 표시됩니다.
  2. 스크롤 위치에 연결된 애니메이션입니다.

다음과 같은 방법으로 경고를 해결할 수 있습니다.

  • 먼저 진행률 표시줄 역할을 할 HTML 요소를 만듭니다. 이 요소는 원하는 대로 스타일을 지정할 수 있습니다.
  • 다음으로 CSS에서 진행률 표시줄의 크기를 조절하는 @keyframes 애니메이션을 정의합니다. 일반적인 접근 방식은 요소를 scaleX(0)에서 scaleX(1)로 확장하는 것입니다.
  • 마지막으로 이 애니메이션을 진행률 표시줄 요소에 적용하고 animation-timeline를 스크롤 타임라인으로 설정합니다. 이렇게 하면 브라우저가 가장 가까운 상위 스크롤러의 스크롤 위치를 기반으로 애니메이션 진행을 구동합니다.

예시 코드

이 코드는 scroll() 함수로 만든 익명의 scroll-timeline을 사용하여 스크롤 시 #progress 요소를 확장합니다.

@media (prefers-reduced-motion: no-preference) {
  @supports ((animation-timeline: scroll())) {
    @keyframes grow-progress {
      from { transform: scaleX(0); }
      to { transform: scaleX(1); }
    }

    #progress {
      position: fixed;
      left: 0; top: 0;
      width: 100%; height: 1em;
      background: red;

      transform-origin: 0 50%;
      animation: grow-progress auto linear;
      animation-timeline: scroll();
    }
  }
}

DOM에서의 위치로 인해 scroll() 함수는 block 방향에서 가장 가까운 상위 스크롤러를 추적합니다. 여기서는 루트 스크롤러입니다.

<body>
  <!-- MANDATORY: Purely decorative visual scroll progress bars MUST set aria-hidden="true" to remove the empty element from the assistive technology reading tree -->
  <div id="progress" aria-hidden="true"></div>
</body>

이 코드는 scroll-timeline 속성으로 생성된 이름이 지정된 스크롤 타임라인을 사용하여 스크롤 시 #progress 요소를 확장합니다.

@media (prefers-reduced-motion: no-preference) {
  @supports ((animation-timeline: scroll())) {
    @keyframes grow-progress {
      from { transform: scaleX(0); }
      to { transform: scaleX(1); }
    }

    :root {
      scroll-timeline: --tl block;
    }

    #progress {
      position: fixed;
      left: 0; top: 0;
      width: 100%; height: 1em;
      background: red;

      transform-origin: 0 50%;
      animation: grow-progress auto linear;
      animation-timeline: --tl;
    }
  }
}

권장사항

스크롤 기반 애니메이션을 사용할 때는 원활하고 접근성 높은 환경을 보장하기 위해 몇 가지 권장사항을 따르는 것이 중요합니다.

  • 권장사항: 기능 감지를 포함합니다. 모든 브라우저가 스크롤 기반 애니메이션을 지원하는 것은 아닙니다. @supports (animation-timeline: scroll())를 사용하여 지원 여부를 확인하고 지원하지 않는 브라우저에 대체 기능을 제공합니다.
    • scroll-timeline-polyfill 패키지는 기능이 완전하지 않고 알려진 문제가 많으므로 대체 전략에 사용하지 마세요.
    • 애니메이션이 장식용으로만 간주되는 경우 점진적 개선을 선택하고 대체 기능을 제공하지 마세요.
  • 권장사항: 보조 기술 읽기 흐름에서 순전히 장식적인 요소는 삭제합니다. 순전히 시각적인 스크롤 표시기에 aria-hidden="true"를 적용하여 스크린 리더가 비어 있고 이름이 지정되지 않은 노드를 만나지 않도록 합니다.
  • 권장사항 사용자 환경설정 존중: 일부 사용자는 웹에서 모션이 적은 것을 선호합니다. prefers-reduced-motion 미디어 쿼리를 사용하여 이러한 사용자의 애니메이션을 사용 중지하거나 줄입니다.
  • 권장사항: 성능이 우수한 CSS 속성에만 애니메이션을 적용하세요. 가장 부드러운 애니메이션을 구현하려면 브라우저의 컴포지터 스레드에서 처리할 수 있는 속성(예: transform 및 opacity)에만 애니메이션을 적용하세요. width 또는 height와 같은 다른 속성에 애니메이션을 적용하면 성능 문제가 발생할 수 있습니다.
  • 권장사항: 올바른 선언 순서를 사용합니다. animation 약식 속성을 사용하는 경우 약식이 타임라인을 재설정하지 않도록 animation-timeline를 뒤에 선언합니다.

scroll() 함수를 사용하여 스크롤 기반 애니메이션을 만들 때 다음을 참고하세요.

  • 선택사항 스크롤러를 명시적으로 지정: 가장 가까운 상위 스크롤러를 타겟팅하지 않는 경우 scroll(root) 또는 scroll(self)를 사용하여 사용할 스크롤러를 명시적으로 지정합니다.
    • root, nearest 또는 self가 충분하지 않은 경우 이름이 지정된 scroll-timeline을 사용합니다.
  • 선택사항 추적할 축을 명시적으로 지정: 기본 block 축을 타겟팅하지 않는 경우 (예: 가로 스크롤러) scroll(block) 또는 scroll(inline)를 사용하여 추적할 축을 명시적으로 지정합니다.

scroll-timeline 속성을 사용하여 스크롤 기반 애니메이션을 만들 때 다음을 수행합니다.

  • 이름에 CSS <dashed-ident>를 사용합니다.
  • 선택사항 추적할 축을 명시적으로 지정: 기본 block 축을 타겟팅하지 않는 경우 (예: 가로 스크롤러) scroll-timeline-axis를 사용하여 추적할 축을 명시적으로 지정합니다.
  • 권장사항: 조회 범위가 작동하는지 확인합니다. scroll-timeline를 선언하는 요소가 애니메이션 요소의 플랫 트리 상위 요소가 아닌 경우 공유 상위 요소에서 timeline-scope를 사용하여 scroll-timeline 이름의 공개 상태를 호이스팅합니다.

대체 전략

스크롤 기반 애니메이션을 지원하지 않는 브라우저의 경우 대체 기능을 사용하여 시각 효과를 다시 만들 수 있습니다. 대체는 일반적으로 스크롤 리스너 (ScrollTimeline 효과의 경우) 또는 IntersectionObserver API (ViewTimeline 효과의 경우)를 사용하여 빌드됩니다.

스크롤 기반 애니메이션을 기본적으로 지원하는 브라우저에서는 항상 네이티브 CSS 구현을 사용하세요. 성능이 더 우수합니다.

대체 접근 방식을 사용하여 모든 효과를 다시 만들 수 있는 것은 아닙니다.

이 사용 사례의 경우 다음 스크립트는 스크롤 기반 애니메이션을 지원하지 않는 브라우저에 대체 기능을 적용합니다. 스크롤 리스너를 사용하여 루트 요소의 스크롤 위치를 추적하고 이에 따라 진행률 표시줄의 transform 속성을 업데이트합니다.

<script>
  if (!CSS.supports('animation-timeline', 'scroll()')) {
    const progress = document.querySelector('#progress');

    window.addEventListener('scroll', () => {
      const scrollable = document.documentElement.scrollHeight - window.innerHeight;
      const scrolled = window.scrollY;
      const progressPercentage = (scrolled / scrollable);

      progress.style.transform = `scaleX(${progressPercentage})`;
    });
  }
</script>