이 글에서는 Scroll-driven animation 에 대한 소개 및 장점에 대해 알아보고 관련 내용 및 사용 예시를 소개합니다.

1. Scroll-driven animation 소개

Scroll-driven animation은 사용자가 스크롤 가능한 컨테이너에서 스크롤이 얼마나 되었는지에 따라 애니메이션 진행이 가능한 새로운 애니메이션 기법입니다. 웹서핑하다 보면, 사이트에서 종종 사용자가 스크롤 한 만큼 애니메이션이 진행되는 사이트들을 접해보신 적이 있을 텐데, 이는 자바스크립트를 사용해 사용자의 스크롤 이벤트에 반응하여 구현되었습니다.

e.g. 자바스크립트를 사용한 애니메이션 구현

See the Pen Javascript 사용 스크롤 애니메이션 예제 by WITBLOG (@witblog) on CodePen.

Scroll-driven animation은 CSS만으로 이러한 애니메이션을 구현할 수 있습니다. 단, 이는 스크롤을 기반으로 애니메이션이 진행되는 방식이며 스크롤 이벤트 트리거 방식과는 다릅니다.

See the Pen animation timeline 사용 스크롤 애니메이션 by WITBLOG (@witblog) on CodePen.

1-1. Scroll-driven animation의 장점

기존방식으로 구현되는 스크롤 기반 애니메이션(자바스크립트 사용)에는 메인 스레드를 이용하는 방식이기에, 두 가지 문제가 있었습니다.

  • 스크롤이 별도의 스레드에서 적용되기 때문에 스크롤 이벤트가 비동기적으로 전달되는 이슈
  • 메인 스레드를 사용하는 애니메이션에서 프레임드랍이 발생하는 이슈

Scroll-driven animation은 CSS애니메이션과 WAAPI(컴포지터 스레드 기반 최적화 API)를 활용하여 메인 스레드의 리소스 사용을 최소화하며 성능이 좋은 스크롤 기반 애니메이션을 구현합니다.

e.g. 각 애니메이션 기술별 성능 비교

특징WAAPICSS AnimationJavascript Animation
메인 스레드 부하낮음 (GPU 기반 컴포지터 스레드)낮음 (GPU 최적화 가능)높음 (주로 메인 스레드 사용)
컴포지터 스레드 활용매우 효과적효과적제한적
제어 및 유연성높음제한적매우 높음

1-2. 지원 브라우저

크롬 115, edge 115 이상 버전에서만 확인가능하며, 파이어폭스, safari에서는 아직 기능하지 않습니다.

2. Animation timelines

애니메이션에서 기본적으로 사용되는 타임라인은 문서 타임라인으로, 페이지 로드 시 시작되고 시간에 따라 증가합니다. 그러나 스크롤 기반 애니메이션이 추가되면서 두 가지 새로운 타임라인이 추가되었습니다.

  • Scroll Progress Timeline : 스크롤 컨테이너의 스크롤 위치에 연결된 타임라인입니다. 사용 시 scroll() 함수를 사용합니다.
  • View Progress Timeline : 스크롤 컨테이너 내부에 위치한 특정 요소의 상대적 위치에 연결된 타임라인입니다. view() 함수를 사용합니다.

animation-timeline 속성을 사용하여 애니메이션 속성과 타임라인을 연결할 수 있습니다. 사용 시, animation 속성을 반드시 먼저 사용해야 적용됩니다.

@keyframes grow {
  from {
    transform: scale(1);
  }
  to {
    transform: scale(2);
  }
}

.box {
  animation: grow linear;        // 애니메이션을 먼저 선언하고, 그 이후 animation-timeline을 적용하여야 합니다.
  animation-timeline: scroll();
}

2-1. Scroll Progress Timeline

<scroll()> =  scroll( [ <scroller> || <axis> ]? )  
<scroller> =  root   | nearest  |  self  |  --custom-timeline
<axis> =   block   |  inline  |  x  |  y  

스크롤 타임라인은 스크롤바 내부의 스크롤의 위치에 따라 애니메이션의 진행도를 결정합니다. 함수 내부에 올 수 있는 값으론 <scroller>, <axis>가 있습니다. 

  • <scroller>는 진행 여부를 결정할 스크롤러를 정하며, root(문서 최상위), nearest(가장 가까운 조상요소), self(자기자신)이 있습니다.
  • <axis>는 축을 나타냅니다. x, y는 각각 x축, y축을 나타내며 block축은 블록차원에서의 축(가로쓰기모드는 세로축, 세로쓰기모드는 가로축), inline은 인라인 축(가로쓰기모드에서는 가로축, 세로쓰기모드에서는 세로축)을 나타냅니다. 기본값은 block 입니다.

아래 예제에서 scroll()함수가 어떻게 적용되는지 확인할 수 있습니다. 

See the Pen scroll timeline function 예제 by WITBLOG (@witblog) on CodePen.

2-2. View Progress Timeline

<view()> =   view( [ <axis> || <'view-timeline-inset'> ]? )  

<axis> =   block   |  inline  |  x  |  y       
<view-timeline-inset> =   [ [ auto | <length-percentage> ]{1,2} ]#  
<length-percentage> =   <length>  |  <percentage>

뷰 타임라인은 스크롤 컨테이너 내 특정 요소의 상대적인 진행률에 따라 애니메이션의 진행도를 결정합니다. 대상이 스크롤 포트에 들어가기 직전부터 시작되어 대상이 스크롤 포트를 완전히 벗어날 때 끝이 납니다.

함수 내부에 올 수 있는 값으론 <axis>, <length-percentage>, 가 있습니다. 

  • <axis>는 축을 나타냅니다. 스크롤 타임라인과 동일하며 기본 값은 block입니다.
  • <view-timeline-inset>은 애니메이션의 시작과 끝시점을 나타냅니다. 퍼센트, 숫자값을 적용할 수 있습니다.

See the Pen view timeline example1 by WITBLOG (@witblog) on CodePen.

See the Pen view timeline example2 by WITBLOG (@witblog) on CodePen.

2-3. animation-range

animation-range = [ <'animation-range-start'> <'animation-range-end'>? ]#  

<animation-range-start> = [ normal | <length-percentage> | <timeline-range-name> <length-percentage>? ]#  
<animation-range-end> = [ normal | <length-percentage> | <timeline-range-name> <length-percentage>? ]#  
<length-percentage> = <length> | <percentage>

animation-range는 스크롤 애니메이션의 범위를 설정하는 속성입니다. 스크롤 애니메이션이 시작하는 지점과 끝나는 지점을 숫자, 퍼센트값 혹은  timeline-range-name 퍼센트 값으로 지정할 수 있습니다. 

timeline-range-name 은 아래와 같습니다.

  • cover : 뷰 진행률 타임라인의 전체 범위를 나타냅니다.
  • entry : 주 상자가 뷰 진행률 가시성 범위에 들어가는 범위를 나타냅니다.
  • exit : 주 상자가 뷰 진행률 가시성 범위를 벗어나는 범위를 나타냅니다.
  • entry-crossing : 주 상자가 끝 테두리 가장자리를 교차하는 범위를 나타냅니다.
  • exit-crossing : 주 상자가 시작 테두리 가장자리를 교차하는 범위를 나타냅니다.
  • contain : 주 상자가 스크롤 포트 내의 뷰 진행률 가시성 범위에 완전히 포함되거나 완전히 포함하는 범위를 나타냅니다. 대상이 스크롤러 보다 길거나 짧은지에 따라 달라집니다.

해당 예시는 animation-range를 다뤄볼 수 있는 가시성 좋은 사이트가 있어 해당 사이트를 참고해보면 좋을 것 같습니다.

3. 사용 예제

지원 브라우저의 범위가 작기 때문에 실 업무에서 쓰이는 일은 아직 없겠지만, 이후에 지원 브라우저의 범위가 추가되어 업무에서도 사용 가능해진다면 적용해 볼 만한 예시들을 소개합니다.

See the Pen scroll – sticky header example by WITBLOG (@witblog) on CodePen.

@keyframes stickyheader {
  to {
    height: 10vh;
    font-size: 10px;
  }
}

.sticky {
  animation: linear both stickyheader;
  animation-timeline: scroll();
  animation-range: 0vh 90vh;
}

스크롤이 내려가면 헤더가 줄어들어 상단 영역에 위치하게 만드는 예시입니다. 전체 스크롤에 맞게 애니메이션이 진행되게 하였고, 범위를 90vh로 두어 최종 헤더의 크기에 맞게 애니메이션이 종료되게 설정하였습니다.

See the Pen 뷰 타임라인 활용 – 2 by 이상호 (@oyyqvrrc-the-sasster) on CodePen.

@keyframes horizontalmove {
  to {
    transform: translateX(calc(-100% + 100vw));
  }
}
.horizontal {
  /* 전체 내용 (타이틀 + 내용3개 + 마지막) 높이  */
  height: 500vh;
}
.content {
  /* 내부의 width값을 전부 더한 값 .text 3개 * 100vw  */
  width: 300vw; 
  animation: linear horizontalmove forwards;
  animation-timeline: --section-pin;
  animation-range: contain 0% contain 100%;
}

특정 시점에서 아래로 스크롤 해도, 우측으로 스크롤 하는 애니메이션입니다.(높이 & 너비 값을 변수로 추가하였다면 더 쉽게 적용할 수 있었을 것 같습니다)
content가 전부 화면에 들어오면 애니메이션이 시작되고 스크롤에 따라 horizontalmove 애니메이션을 적용하게 하였습니다.

See the Pen 뷰 타임라인 활용 – 1 by WITBLOG (@witblog) on CodePen.

@keyframes emp {
  0% {
    transform: translateX(-100%);
    z-index: 1;
  }
  35% {
    transform: translateX(0);
    z-index: 1000;
  }
  50% {
    transform: scale(1.5);
  }
  65% {
    transform:translateX(0);
    z-index: 1;
  }
  100% {
    transform: translateX(100%);
  }
}
.item {
  transform: translateX(-100%);
  animation: emp linear both;
  animation-timeline: view(inline);
}

가로 스크롤을 사용해서 요소가 중앙에 올 경우 스케일로 확대하는 애니메이션입니다.

4. 참조

카테고리: Research

0개의 댓글

답글 남기기

아바타 플레이스홀더

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다