스크롤 시 헤더 축소

스크롤 시 축소되는 헤더는 사용자가 아래로 스크롤할 때 페이지 상단의 고정된 헤더 요소가 더 작은 크기로 부드럽게 전환되는 일반적인 UI 패턴입니다. 이 효과는 필수 탐색 또는 브랜딩 요소에 액세스할 수 있도록 유지하면서 기본 콘텐츠의 화면 공간을 극대화하는 데 자주 사용됩니다. CSS 스크롤 기반 애니메이션을 사용하면 애니메이션을 문서의 스크롤 위치에 연결하여 선언적이고 성능이 우수한 방식으로 이 효과를 얻을 수 있습니다.

구현 방법

스크롤 시 축소되는 헤더를 만드는 방법은 다음과 같습니다.

  1. 고정 헤더 만들기: 페이지 상단에 고정되고 높이가 미리 정의된 헤더 요소로 시작합니다.

    <header>HEADER</header>
    
    header {
      position: fixed;
      height: 200px;
      top: 0;
      left: 0;
      right: 0;
    }
    
  2. 축소 애니메이션 정의: 헤더의 높이를 변경하는 CSS 애니메이션을 만듭니다.

    @keyframes shrink {
      to {
        height: 50px;
      }
    }
    
  3. 애니메이션 및 스크롤 타임라인 적용: 애니메이션을 헤더에 연결하고 scroll() 함수를 사용하여 문서의 스크롤 위치에 연결합니다.

    header {
      animation: shrink auto linear both;
      animation-timeline: scroll(block root);
    }
    
  4. animation-range 설정: animation-range 속성을 사용하여 애니메이션이 발생해야 하는 스크롤 거리를 지정합니다. 예를 들어 스크롤의 처음 150픽셀에서 헤더를 축소하려면 animation-range: 0px 150px;를 사용합니다.

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

도움말: 헤더 뒤에 오는 콘텐츠가 헤더에 가려지지 않도록 하려면 헤더의 초기 높이와 동일한 padding-top를 body (또는 기본 콘텐츠 컨테이너)에 추가하세요.

팁: 페이지 콘텐츠가 축소되는 헤더와 동기화되어 스크롤되도록 하려면 animation-range-end를 시작 크기와 종료 크기의 차이로 설정하세요. 이렇게 하면 헤더가 최종 크기에 도달할 때 애니메이션이 정확하게 완료됩니다. 이 데모에서는 헤더가 200px에서 50px로 축소되므로 animation-range-end가 150px로 설정됩니다.

예시 코드

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

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

권장사항

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

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

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

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

이 사용 사례의 경우:

  • animation-range에서 백분율을 사용할 때 애니메이션을 적용하는 요소는 position: static 또는 position: relative가 아니어야 합니다.
    • 이러한 요소는 '인플로우'로 간주되기 때문입니다. 스크롤할 때 이러한 요소를 축소하면 총 스크롤 거리가 줄어들어 스크롤에 대한 계산된 값(예: 10%)에 영향을 미칩니다.

브라우저 지원 및 대체 전략

따라서 일반적으로 대체 전략이 필요합니다.

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

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

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

이 사용 사례의 경우 다음 스크립트는 스크롤 기반 애니메이션을 지원하지 않는 브라우저에 대체 기능을 적용합니다. 스크롤 리스너를 사용하여 150px 거리에서 문서의 스크롤 위치를 추적하고 이에 따라 헤더의 높이를 업데이트합니다.

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