CSS 기반 Scroll UX

최근 W3C는 CSS를 활용한 scroll ux 구현이 가능하도록 표준을 만들고 있습니다. 관련 사양을 살펴보고 인사이트를 얻어 봅니다.

2023-08-26

소개

스크롤을 이용한 에니메이션은 보통 다음 두 가지에 속합니다.

  • 사용자 스크롤에 따른 에니메이션
  • 특정 엘리먼트가 viewport 안에 들어오거나 나갈때

Scroll-driven Animations specification 스펙은 위 두가지 형식을 지원합니다.

animation-timeline

다양한 스크롤 UX는 결국 animation-timeline 값을 scroll을 기준으로 조정하는데 있습니다.

일반적으로 @keyframes를 사용하여 정의한 에니메이션은 이미 지정한 duration값에 따라 특정 시간동안 frame을 재생합니다.

이 animation frame을 scroll에 맞추어 앞뒤로 재생함으로서 scroll 기반의 ux와 에니메이션을 구현할 수 있습니다.

여기서 animation-timeline CSS 프로퍼티에 scroll 진행률이나 특정 엘리먼트의 진입/퇴출 비율을 전달함으로서 animation의 frame을 결정할 수 있습니다.

사용자 스크롤에 따른 에니메이션 UX

Scroll-driven Animations: Progress Bar: Anonymous Anonymous Scroll Timeline with scroll(block root)

상단에 문서의 스크롤 위치를 쉽게 알아볼 수 있는 progress bar을 예로 들어보겠습니다. progress bar 엘리먼트를 fixed postion하고 그 너비를 0에서부터 100%까지 animation 하는 keyframe을 정의할 수 있습니다. 이제 남은 것은 progress bar 엘리먼트의 animation-timeline 값을 스크롤 진행상황과 연동하는 것 뿐 입니다.

scroll()

animation-timeline: scroll(block root)와 같이 scroll() CSS 함수를 사용하면 좀더 세밀하게 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(block root);
}

scroll() CSS 함수는 상위 컴포넌트와 디커플링되어 있는 경우, 스크롤 이벤트를 받아올 엘리먼트를 독립적으로 지정할 수 있습니다. 이러한 특성으로 동적으로 어느 곳에서나 하위 엘리먼트로 붙여 동작이 가능하지만, 상대적으로 유지보수가 까다로와 질 수 있겠습니다.

scroll-timeline

scroll-timeline: <name>을 정의한 엘리먼트의 스크롤 이벤트를 <name>에 정의한 이름으로 접근할 수 있습니다.

html {
  scroll-timeline: --page-scroll block;
}

@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: --page-scroll;
}

위의 예제는 <name> 값으로 --page-scroll을 사용하고 있습니다. --page-scroll이라는 명시적인 이름을 사용하기 때문에 scroll 이벤트를 제공하는 엘리먼트와 이를 animation-timeline에서 사용하는 엘리먼트를 쉽게 찾을 수 있습니다. 다시말해 런타임 시에 결정되는 것이 아닌 실행 전 선언적으로 이들의 관계를 파악할 수 있습니다.

이러한 특징으로 scroll 이벤트를 받아 처리하는 컴포넌트가 독립적으로 동작하도록 구성하기에 유연성이 떨어질 수도 있겠습니다.

특정 엘리먼트가 viewport 안에 들어오거나 나갈때의 UX

view()

통상 animation-timeline: view() CSS 함수를 사용하여 엘리먼트가 viewport 영역에 얼마만큼 나타났는지에 따라 에니메이션 keyframe을 조정할 수 있습니다.

다음 예제는 variable font와 animation-timeline: view()를 사용하여 재미있는 경험을 제공합니다.

view-timeline

view-timeline-name: <name>을 정의한 엘리면트의 viewport 진/출입 비율을 <name>을 사용하여 접근할 수 있습니다.

@keyframes reveal {
  from {
    opacity: 0;
    clip-path: inset(45% 20% 45% 20%);
  }
  to {
    opacity: 1;
    clip-path: inset(0% 0% 0% 0%);
  }
}

.revealing-image {
  /* Create View Timeline */
  view-timeline-name: --revealing-image;
  view-timeline-axis: block;

  /* Attach animation, linked to the  View Timeline */
  animation: linear reveal both;
  animation-timeline: --revealing-image;

  /* Tweak range when effect should run*/
  animation-range: entry 25% cover 50%;
}

view-timeline을 사용하면 자기 자신 뿐만 아니라 다른 엘리먼트의 viewport 진/출입 비율을 기반으로 animation을 수행할 수 있습니다.

마치며

간단하게 새로운 scroll 에니메이션과 관련된 css 사양들을 살펴 보았습니다.

W3C의 CSS 사양은 업계에서 시도해온 여러 사용자 경험을 구현할 수 있는 최적화된 사양을 정의하는 편 입니다. 어떻게 하면 사용자들이 쉽고 직관적으로 사용할지에 대한 고민의 결과이기도 합니다. 따라서 트렌드로 자리잡고 있는 Scroll 기반의 UX를 어떠한 방식으로 CSS를 사용하여 풀었는지 살펴보는 것은 의미가 있습니다.

해당 스펙이 하루빨리 안정화 되어 앞으로도 많은 Scroll UX 아이디어가 나타나길 기대해 봅니다.

Loading script...