이 글에서는 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. 각 애니메이션 기술별 성능 비교
특징 | WAAPI | CSS Animation | Javascript 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);
}
가로 스크롤을 사용해서 요소가 중앙에 올 경우 스케일로 확대하는 애니메이션입니다.
0개의 댓글