요소가 스크롤 포트에 들어가거나 나올 때 요소에 시작 및 종료 효과를 추가합니다.

시작 및 종료 효과는 요소가 표시 영역에 들어가거나 나갈 때 트리거되는 애니메이션입니다. 이를 사용하여 매력적이고 동적인 사용자 환경을 만들 수 있습니다. 예를 들어 시작 효과를 사용하여 요소가 스크롤되어 표시될 때 페이드 인하거나 종료 효과를 사용하여 요소가 스크롤되어 사라질 때 축소할 수 있습니다.

구현 방법

요소에 시작 및 종료 효과를 추가하려면 몇 가지 CSS 속성을 결합해야 합니다. 아래 방법을 참조하십시오.

  1. 들어가기 및 나가기 애니메이션에 별도의 @keyframes를 만듭니다. 들어가기 애니메이션은 요소가 표시 영역에 들어갈 때 적용되고 나가기 애니메이션은 요소가 표시 영역을 나갈 때 적용됩니다.

    @keyframes slide-in {
      from { transform: translateX(-100%); }
    }
    @keyframes slide-out {
      to { transform: translateX(100%); }
    }
    
  2. 요소에 시작 및 종료 키프레임을 연결합니다. animation 속성에 여러 애니메이션을 정의하면 됩니다.

    • 애니메이션이 시작되기 전에 초기 상태가 적용되도록 시작 애니메이션에 backwards의 animation-fill-mode를 지정합니다.
    • 애니메이션이 완료된 후 최종 상태를 유지하도록 종료 애니메이션에 forwards의 animation-fill-mode를 지정합니다.
    .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;
    }
    

예시 코드

이 코드는 익명 view-timeline을 사용하여 스크롤 시 스크롤러의 직계 하위 요소에 애니메이션을 적용합니다.

@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 애니메이션이 재생됩니다.

다음 코드는 시각적 결과는 동일하지만 이름이 지정된 view-timeline을 사용하여 스크롤 시 스크롤러의 직계 하위 요소에 애니메이션을 적용합니다.

@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) 검사를 포함해야 합니다(MUST).
    • 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를 선언하는 요소가 애니메이션 요소의 플랫 트리 상위 요소가 아닌 경우 공유 상위 요소에서 timeline-scope를 사용하여 view-timeline 이름의 공개 상태를 호이스팅합니다.

추적된 주제의 여러 요소 또는 하위 요소를 애니메이션화해야 하는 경우 이름이 지정된 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>