[번역] CSS만으로 스크롤 기반 애니메이션 구현하기

Saetbyeol·2025년 7월 25일
44

translations.zip

목록 보기
25/25

원문: https://webkit.org/blog/17101/a-guide-to-scroll-driven-animations-with-just-css/

CSS 애니메이션은 2007년 애플이 웹에 처음 도입한 이후로 많은 발전을 거듭해 왔습니다. 단순한 색상 전환에서 시작했던 애니메이션은 이제 아름답고 정교한 이미지가 화면 위를 회전하고 날아다니는 수준까지 이르렀습니다.

하지만 이런 애니메이션을 스크롤과 같은 사용자 동작에 연결하려면 서드파티 라이브러리와 상당한 양의 자바스크립트 코드가 필요했기 때문에 다소 복잡해질 수밖에 없었습니다. 하지만 이제 단 몇 줄의 CSS만으로도 스크롤 기반 애니메이션을 만들 수 있습니다.

스크롤 기반 애니메이션은 브라우저 지원이 확대되고 있으며 Safari 26 베타 버전에서 사용할 수 있으므로 사용자의 눈길을 사로잡는 효과를 더 쉽게 구현할 수 있습니다. 지금부터 그 방법을 보여드리겠습니다.

먼저 스크롤 기반 애니메이션이 어떻게 구성되는지 분석해 보겠습니다.

스크롤 기반 애니메이션은 세 가지 요소로 구성됩니다.

타겟, 키프레임, 타임라인 세 영역이 그려져 있는 직사각형

  • 타겟: 애니메이션을 적용할 페이지 내 대상
  • 키프레임: 사용자가 스크롤 할 때 요소에 일어나는 변화
  • 타임라인: 애니메이션의 진행을 결정하는 기준

여기서 두 가지는 이미 익숙한 개념일 겁니다.

페이지에서 움직이게 하고 싶은 어떤 요소든 타겟이 될 수 있으며, 원하는 스타일로 자유롭게 꾸밀 수 있습니다.

키프레임은 수년간 사용되어 온 전통적인 CSS 애니메이션입니다. 멋진 스크롤 기반 애니메이션을 만드는 것은 이 키프레임을 얼마나 잘 설계하느냐에 달려 있습니다. CSS 애니메이션이 처음이라면, MDN의 자료를 참고해 보세요.

세 번째 요소인 타임라인은 상대적으로 생소한 개념일 수 있지만, 스크롤 기반 애니메이션에서 아주 중요한 역할을 합니다. 이제 이 부분을 좀 더 자세히 살펴보겠습니다.

타임라인이란?

애니메이션은 시작, 중간, 끝이 있으며 타임라인을 따라 순차적으로 진행됩니다. 웹에서 기본적으로 사용되는 타임라인은 시간 기반의 문서 타임라인(document timeline)입니다. 즉, 시간이 흐름에 따라 타임라인도 함께 진행됩니다.

기본 타임라인을 사용한다면, 애니메이션은 시간의 흐름에 따라 실행됩니다. 예를 들어 기본 타임라인으로 초록색 원의 색상을 바꾸는 애니메이션이라면, 1초에는 빨간색으로, 2초에는 파란색으로, 3초에는 노란색으로 바꿀 수 있습니다. 이처럼 시간에 맞춰 색상이 변화하는 것이죠.

이것이 수년간 애니메이션이 동작해 온 방식입니다. 그러다 2023년 6월, CSS Animations Level 2 사양의 일부로 animation-timeline 속성이 도입되면서 변화가 찾아왔습니다. 이 속성 덕분에 시간 외에도 다른 요소를 애니메이션에 동력으로 활용할 수 있게 되었고, 그중 하나가 사용자의 스크롤 동작입니다. 이로써 스크롤 기반 애니메이션이 가능해진 것입니다.

스크롤 기반 애니메이션에서는 더 이상 '시간'을 사용하지 않습니다. 대신, 새롭게 등장한 두 가지 타임라인 유형인 scroll과 view를 사용합니다.

scroll() 타임라인

스크롤 타임라인에서는 시간의 흐름이 아닌 사용자의 스크롤에 따라 애니메이션이 진행됩니다.

사용자가 스크롤을 시작하면 애니메이션이 시작되고, 스크롤이 멈추면 애니메이션도 멈춥니다. 이 새로운 타임라인이 스크롤과 애니메이션을 연결합니다.

스크롤 기반 애니메이션을 설명할 때 자주 사용하는 예시가 진행바(progress bar)입니다. 실제로는 이미 스크롤바가 있어 진행바가 꼭 필요한 것은 아니지만, 이해하기 쉬운 예제이므로 함께 만들어 보겠습니다.

먼저 해야 할 일은 애니메이션을 적용할 타겟을 만드는 것입니다.

‘A-School of Code’라는 코딩 학교 웹 사이트에 타겟을 하나 만들어 보겠습니다. 만약 페이지 하단에 진행 바를 만들려면, 푸터의 의사 요소(pseudo element)로 추가할 수 있습니다. 이 진행바는 페이지 왼쪽 하단에서 시작해 오른쪽으로 채워지도록 만들고자 합니다.

코드는 다음과 같이 작성하면 됩니다.

footer::after {
  content: "";
  height: 1em;
  width: 100%;
  background: rgba(254, 178, 16, 1);
  left: 0;
  bottom: 0;
  position: fixed;
}

그러면 페이지를 가로지르는 좁은 노란색 막대가 표시됩니다. 아래 이미지에서 분홍색 화살표로 가리키고 있습니다.

검은색 배경 페이지 하단에 노란색 얇고 긴 막대가 가로질러 있으며 분홍색 화살표가 노란색 막대를 가리키고 있음

이제 실제 애니메이션을 생성하기 위해 키프레임이 필요합니다.

키프레임에 이름을 붙일 건데, "progress-expand"라고 하겠습니다.

@keyframes progress-expand {
  from {
    width: 0%;
  }
  to {
    width: 100%;
  }
}

새로운 타임라인 scroll()을 사용하는 것이 세 번째 단계입니다. 사용자가 스크롤하는 동안에만 애니메이션이 적용되도록 브라우저에 알려줌으로써 스크롤 기반 애니메이션이 완성됩니다.

footer::after {
  ...
  animation-timeline: scroll();
}

마지막으로 아래처럼 진행 바에 animation 속성을 추가하여 세 가지 요소를 하나로 결합해야 합니다.

footer::after {
  ...
  animation: progress-expand;
  animation-timeline: scroll();
}

animation-timeline 속성은 animation 속성 뒤에 설정해야 하며, 그렇지 않으면 작동하지 않는다는 점에 주의하세요.

자, 이렇게 해서 여러분의 첫 스크롤 기반 애니메이션이 완성되었습니다.

페이지에 움직임을 추가했으니, 출시하기 전에 한 가지 꼭 고려해야 할 점이 있습니다.
방금 추가한 움직임 때문에 사용자가 불편을 느끼진 않을까요? 웹 접근성에 문제는 없을까요?

진행바처럼 미묘하고 느린 움직임은 시야에 크게 영향을 주지 않기 때문에 불편함을 일으킬 가능성은 낮습니다. 반면, 크고 시선을 많이 끄는 애니메이션은 시차 효과(parallax), 확대, 초점 흐림(depth-of-field) 같은 기법을 사용해 3차원 공간의 움직임을 시뮬레이션하는 경우가 많습니다. 움직임에 민감한 사용자는 큰 애니메이션을 실제 3차원 공간에서의 움직임으로 인식해 불편함이나 어지러움 같은 부작용을 겪을 가능성이 더 높습니다.

이런 우려가 있을 때는, 다음과 같이 '애니메이션 동작 줄이기(prefers-reduced-motion)' 설정을 감지하는 미디어 쿼리로 애니메이션을 감싸 주는 게 좋습니다.

@media not (prefers-reduced-motion) {
  /* 여기에 애니메이션을 작성합니다 */
}

이렇게 하면 사용자가 '애니메이션 동작 줄이기'를 설정하지 않은 경우에만 애니메이션이 실행됩니다. 이 설정을 사용하는 경우를 자세히 알아보려면 움직임을 위한 반응형 디자인 문서를 참고하세요. 이렇게 처리하면 애니메이션은 안전하다고 생각합니다.

최종 결과물은 다음과 같습니다.

코드를 한눈에 보면 이렇습니다.

footer::after {
  content: "";
  height: 1em;
  width: 100%;
  background: rgba(254, 178, 16, 1);
  inset-inline-start: 0;
  bottom: 0;
  position: fixed;
  transform-origin: top left;
  animation: grow-progress linear;
  animation-timeline: scroll();
}

@keyframes grow-progress {
  from {
    transform: scaleX(0);
  }
  to {
    transform: scaleX(1);
  }
}

view() 타임라인

위 예제에서 사용한 scroll() 타임라인은 사용자가 스크롤을 시작하는 즉시 활성화됩니다. 화면에 보이는 요소 또는 타겟이 뷰포트에 등장하는 시점과 상관없이 동작합니다.

이를 의도하는 경우도 있지만, 대부분의 경우에는 타겟 요소가 화면에 나타날 때 애니메이션이 실행되길 원합니다.

타겟은 캐러셀, 메뉴, 이미지 갤러리 등 무엇이든 될 수 있습니다. 하지만 애니메이션의 타겟 요소가 항상 화면에 떠 있는 경우는 드뭅니다. 대신 사용자가 사이트를 탐색하며 아래로 스크롤 할 때, 뷰포트에 타겟이 들어오면서 점차 모습을 드러냅니다.

따라서 단순히 사용자가 스크롤을 시작했을 때 타임라인을 활성화하는 대신, 타겟이 뷰포트에 등장할 때 타임라인이 활성화 시켜야 하는 경우가 있습니다. 이를 위해서는 다른 타임라인이 필요합니다. 바로 view() 타임라인입니다.

이 타임라인이 어떻게 작동하는지 알아보기 위해, 스크롤 하면서 이미지가 뷰포트에 들어올 때 슬라이드로 나타나는 간단한 예제를 살펴보겠습니다.

우선, 더미 텍스트가 들어간 간단한 아티클을 만들고 페이지의 여러 위치에 이미지를 몇 장 배치하겠습니다. 이 이미지들은 문서 하단에 있기 때문에, 페이지가 처음 로드될 때는 뷰포트에 보이지 않습니다.

스크롤 기반 애니메이션에 필요한 세 가지 요소를 다시 살펴보겠습니다.

  • 타겟: 애니메이션을 적용할 페이지 내 대상
  • 키프레임: 사용자가 스크롤 할 때 요소에 일어나는 변화
  • 타임라인: 애니메이션의 진행을 결정하는 기준

이번 예시에서 타겟은 HTML에 있는 아티클의 이미지들입니다. 좋습니다! 하나는 해결했고 이제 두 개가 남았습니다.

이제 키프레임을 설정해야 합니다. 스크롤 기반 애니메이션에서 실제로 움직임을 정의하는 부분입니다.

여기서 두 가지 효과를 주고 싶습니다. 이미지가 서서히 나타나면서, 오른쪽에서 슬라이드 되며 들어오도록 만드는 것입니다. 이 두 효과를 아래 코드로 구현할 수 있습니다.

@keyframes slideIn {
  0% {
    transform: translateX(100%);
    opacity: 0;
  }
  100% {
    transform: translateX(0%);
    opacity: 1;
  }
}

마지막으로 뷰포트에 들어왔을 때 활성화 시키기 위해 이미지 태그에 animation-timeline 속성을 설정해야 합니다.

img {
  animation-timeline: view();
}

이제 세 요소가 완성되었으므로 이미지에 animation 속성을 설정하여 모두 하나로 모아야 합니다.

다시 한번 강조하지만, animation 속성을 먼저 설정하는 것이 중요합니다. 그렇지 않으면 작동하지 않습니다.

img {
  animation: slideIn;
  animation-timeline: view();
}

이렇게 하면 우리가 원하던 멋진 슬라이드 효과를 얻을 수 있습니다.

하지만 이 애니메이션에 한 가지 더 수정하고 싶은 부분이 있습니다. 지금 상태에서는 이미지가 거의 뷰포트 밖으로 나갈 때까지 계속 슬라이드 되고 있다는 점을 눈치채셨을 겁니다. 즉, 이미지가 화면에 보이는 내내 움직이고 있는데 이는 사용자에게 그다지 좋은 경험은 아닙니다.

우리가 진짜로 원하는 건, 이미지가 자리에 슬라이드 되며 등장하고 일정 시간 그 자리에 머무르는 것입니다. 그래야 사용자가 불필요한 움직임에 방해받지 않고 이미지를 온전히 볼 수 있습니다. 이때 필요한 것이 바로 animation-range 속성입니다.

animation-range 속성은 애니메이션의 시작 시점과 종료 시점을 브라우저에 알려주는 역할을 합니다. 기본값은 0%에서 100%입니다. 여기서 0%는 대상 요소가 뷰포트에 처음 들어오는 순간, 100%는 대상 요소가 뷰포트를 완전히 빠져나가는 순간을 의미합니다.

위 예시에선 animation-range를 따로 지정하지 않았기 때문에 기본값이 적용됩니다. 그래서 이미지의 첫 픽셀이 뷰포트에 들어오는 순간 애니메이션이 시작되고, 마지막 픽셀이 뷰포트를 벗어날 때까지 계속 움직입니다.

사용자가 이미지를 더 편하게 볼 수 있도록 하려면, 애니메이션을 뷰포트의 약 절반 지점에서 멈추도록 설정하는 게 좋습니다. 그 시점에서 이미지가 제자리를 찾고, 그 자리에 그대로 머무르게 만듭니다. 이를 위해 아래 코드와 같이 animation-range 값을 0%에서 50%로 변경합니다.

img {
  animation: slideIn;
  animation-timeline: view();
  animation-range: 0% 50%;
}

제가 원하던 대로 이제 이미지가 페이지의 절반 정도 올라가면 애니메이션이 멈춥니다.

변화의 차이가 느껴지시나요? 이미지 보는 게 더 쉬워지셨나요? 혹시 값을 조정하는 게 더 나을 것 같나요? 이렇게 질문을 계속 던져보면 이러한 변화가 사용자에게 어떤 의미가 있는지 더 잘 이해할 수 있습니다. 그러니 잠시 시간을 내어 고민해 보는 것은 좋은 습관입니다.

출시하기 전에 고려해야 할 또 다른 사항은 이 애니메이션이 움직임에 민감한 사용자에게 미치는 영향입니다. 위에서 언급했던 것과 마찬가지로 페이지에 움직임을 도입하는 것이므로 불편함을 유발하지는 않는지 확인해야 합니다.

이번에는 진행바보다 더 큰 애니메이션이 있습니다. 페이지의 이미지가 꽤 큰데, 이미지가 움직이리라 예상하지 못해 너무 빨리 스크롤 하면 이미지가 축소될 수 있습니다. 이는 불편함을 유발할 수 있습니다. 안전을 위해 '애니메이션 동작 줄이기' 쿼리에 넣을 예정이므로 최종 코드는 다음과 같습니다.

img {
  @media not (prefers-reduced-motion) {
    animation: slideIn;
    animation-timeline: view();
    animation-range: 0% 50%;
  }
}

이제 출시할 수 있겠군요.

한 단계 더 들어가서

animation-timelineanimation-range를 활용해 더 다양한 작업을 할 수 있습니다. scroll()view()는 함수이므로 값을 넘겨주어 스크롤 기반 애니메이션의 동작을 더욱 세밀하게 조정할 수 있습니다. 기본 스크롤러 요소를 변경하는 것도 가능합니다. 스크롤러 요소란, 타임라인이 설정되는 스크롤바가 있는 요소를 말합니다. 아무 값도 넘기지 않으면 nearest가 기본값이며, 가장 가까우면서 스크롤 가능한 상위 요소가 지정됩니다. 하지만 이 값을 rootself로도 지정할 수 있습니다. 스크롤바의 축도 조정할 수 있습니다. 기본값은 block이지만, inline, x, y도 가능합니다. 직접 값을 바꿔 보면서 어떤 효과가 있는지 확인해 보세요.

또한 애니메이션 요소의 화면 진입, 퇴장 시점을 세밀하게 조정하여 연출을 더욱 정밀하게 만들 수도 있습니다. 이 부분은 추후 포스팅으로 자세히 다룰 예정입니다. 그동안은 Safari 26 베타에서 scroll과 view 타임라인을 자유롭게 실험해 보며, 여러분의 웹사이트나 웹 앱에서 인터랙션 수준을 한 단계 끌어올려 보세요.

스크롤 기반 애니메이션을 적용해 보신 후에는 여러분의 생각을 들려주세요. Saron Yitbarek에게 BlueSky로 메시지를 보내시거나, 다른 담당자 Jen Simmons(Bluesky/Mastodon), Jon Davis(Bluesky/Mastodon)에게도 연락해 보세요. WebKit의 링크드인 계정을 팔로우 하셔도 좋습니다. 버그나 문제가 발견되면 WebKit 버그 리포트를 제출해 주세요.

7개의 댓글

comment-user-thumbnail
2025년 7월 31일

This translation on implementing scroll-based animation using only CSS shows how powerful and lightweight modern styling can be without relying on JavaScript. Much like how the El Pollo Loco menu https://elpollolocomenus.net/ is designed for quick yet flavorful choices, CSS scroll animations offer smooth, efficient effects with minimal code. A great example of doing more with less in front-end design.

답글 달기
comment-user-thumbnail
2025년 7월 31일

Creating scroll-based animations using only CSS shows how powerful styling alone can be in enhancing user experience. Similarly, Shadow Fight 2 Mod APK https://fightshadow2apk.com/ enhances gameplay by offering unlocked levels and advanced visuals without needing extra in-game purchases.

답글 달기
comment-user-thumbnail
2025년 8월 1일

스크롤 기반 CSS 애니메이션은 이제 animation-timeline 속성 덕분에 몇 줄의 코드만으로도 구현 가능하며, Safari 26 베타부터 지원됩니다. 타겟, 키프레임, 타임라인 세 요소를 활용해 사용자 스크롤에 반응하는 인터랙티브한 효과를 만들 수 있어 웹 디자인의 표현력이 크게 향상됩니다. Bible Chat App

답글 달기
comment-user-thumbnail
2025년 8월 4일

ReadChainsawManManga.com is a fantastic, fast-loading site to dive into Chainsaw Man. The clean reading interface makes it easy to flow through chapters, and the art quality really shine
https://ww.readchainsawmanmanga.com/

답글 달기
comment-user-thumbnail
2025년 8월 4일

thebluelockmanga.com is a sharp and reliable site to read Blue Lock. Its fast-loading pages and clean interface make it easy to follow every intense match and character showdown.
https://ww1.thebluelockmanga.com/

답글 달기
comment-user-thumbnail
2025년 8월 4일

stumble-guyzapk.com is a great site for fans of Stumble Guys who want access to older versions and modded APKs with unique features like unlocked skins and unlimited gems.
https://stumble-guyzapk.com/

답글 달기