JS로 애니메이션 삼국지 통일 (WAAPI : Web Animation API )

Web Animations API(이하 WAAPI)는 자바스크립트로 애니메이션을 정교하게 제어할 수 있는 표준 웹 API입니다.
이것이 낯설다해도 걱정하지 마세요.
사실 WAAPI는 새로운 무언가를 더하는 기술이 아닙니다.
오히려 각자 따로 놀던 CSS, JS, SVG의 애니메이션 방식을 하나의 통일된 방식으로 묶기 위해 만들어진 API입니다.
기존 애니메이션 방식들은 왜 통합이 필요했을까?
1. CSS 애니메이션 / 트랜지션
@keyframes spin { ... }
div { animation: spin 2s infinite; }
- 장점 : 간단하고 성능 좋음
- 단점 : JS로 세밀하게 제어하기 매우 어려움
2. SVG 애니메이션
<animate attributeName="cx" dur="2s" ... />
- SVG 전용 애니메이션 문법
- CSS와 문법도 다르고 JS 제어도 다름
- 일부 브러우저 지원 불완전
3. JS requestAnimationFrame
function loop(t) {
element.style.transform = ...
requestAnimationFrame(loop)
}
- 장점 : 완전한 제어 가능
- 단점 : 성능 최적화를 개발자가 직접 해야 함 (프레임 드랍, CPU사용 증가 등)
위처럼 애니메이션 방식마다 문법도 다르고 제어 방식도 다르고 성능 특성도 달라 복잡했습니다.
그래서 등장한 것이 바로 WAAPI, 웹 애니메이션의 '공통 언어'입니다.
WAAPI가 제공하는 통합된 애니메이션 방식 :
1. CSS keyframes와 동일한 문법을 JS에서 그대로 사용
elem.animate([
{ opacity: 0 },
{ opacity: 1 }
], 500);
2. SVG요소에도 동일한 방식으로 적용 가능하고
svgElement.animate([...], options);
3. JS에서 가능한 세밀한 제어가 가능하며!
player.pause(); //애니메이션 멈추기
player.reverse(); //애니메이션 역재생
player.currentTime = 1000; //애니메이션 점프
player.playbackRate = 2; //애니메이션 재생 속도 2배로
4. CSS처럼 하드웨어 가속도 적용된다.
한마디로 웹에서는 WAAPI가 CSS·SVG·JS를 하나의 방식으로 통일한 애니메이션 표준 이라고 보면 됩니다.
CSS → WAAPI로 변환해보기
실제 사용을 통해 WAAPI와 CSS가 얼마나 비슷한지 살펴봅시다.
아래의 CSS코드를 자바스크립트로 바꿔보겠습니다.
@keyframes emphasis {
0% { transform: scale(1); opacity: 1; }
30% { transform: scale(.5); opacity: .5; }
78.75% { transform: scale(.667); opacity: .667; }
100% { transform: scale(.6); opacity: .6; }
}
#toAnimate {
animation: emphasis 700ms ease-in-out 10ms infinite alternate forwards;
}
CSS keyframes에 작성한 내용을 animate메서드 첫번째 인자의 배열로 전달하고,
animation에 작성한 내용은 두번째 인자에 오브젝트로 전달합니다. 사실상 다른 점이 거의 없죠?
const div = document.getElementById("toAnimate");
div.animate(
[
{ transform: "scale(1)", opacity: 1 },
{ transform: "scale(.5)", opacity: 0.5 },
{ transform: "scale(.667)", opacity: 0.667 },
{ transform: "scale(.6)", opacity: 0.6 },
],
{
iterationStart: 0,
duration: 700,
easing: "ease-in-out",
delay: 10,
iterations: Infinity,
direction: "alternate",
fill: "forwards",
},
);
기본 사용법
아래와 같이 애니메이션을 실행하면 Animation객체가 반환됩니다.
var player = document.getElementById('square').animate([{ transform: 'scale(1)', opacity: 1 },{ transform: 'scale(.5)', opacity: 0.5 }], {});
console.log(player); //Animation객체
이렇게 Animation객체를 변수에 저장해두면
애니메이션이 실행되는 동안에 play, pause, reverse 등 여러 재밌는 조작을 할 수 있습니다.
animation함수는 두 개의 인자를 받습니다 :
- KeyframeEffect 객체들의 배열
- 이 부분은 CSS의 @keyframes안에 정의하던 값과 동일한 역할을 합니다.
- AnimationEffectTimingProperties 옵션
- 이건 CSS의 animation-* 속성들 (또는 shorthand animation 속성)과 동일한 역할을 합니다.
💡 Good to know
animation()함수는 WAAPI에서 새로 추가된 기능이라, 사용하기 전에 브라우저 지원 여부를 체크하거나
polyfill을 포함해야 할 수도 있습니다.
애니메이션 상태(playState)
element.animate()를 호출하면 리턴되는 Animation객체를 통해
읽기 전용 속성인 playState를 확인하면, 현재 애니메이션이 어떤 상태인지 알 수 있습니다.
var player = element.animate(/* ... */);
console.log(player.playState); // "running"
상태 종류:
- "idle"
- "running"
- "paused"
- "finished"
- "pending"
상태 변경:
player.pause(); // playState → "paused"
player.play(); // playState → "running"
player.cancel(); // playState → "idle" (애니메이션 초기 상태로 돌아감)
player.reverse(); // 이건 역재생
player.finish(); // playState → "finished" (애니메이션 끝 상태로 이동)
애니메이션 재생 속도 제어(playbackRate)
애니메이션 속도를 조절하는 기능은 Animation객체가 가진 읽기/쓰기 가능한
playbackRate속성으로 구현할 수 있습니다.
var player = element.animate(/* ... */);
player.playbackRate = 2; // 2배 속도
player.playbackRate = 0.5; // 절반 속도
타임라인(currentTime)
애니메이션은 일정한 타임라인을 가집니다.
animation.currentTime
currentTime은 애니메이션이 현재 타임라인의 몇 ms지점에 있는지를 나타냅니다.
이 값의 최댓값은 아래처럼 계산됩니다 :
delay + (duration × iterations)
예를 들어, 애니메이션의 설정이 아래와 같이 되어 있다면 :
- duration: 1000 (총 1초짜리 애니메이션)
- delay: 0
- iteration: 1 (한 번만 반복)
이 애니메이션의 타임라인 총 길이는 1000ms입니다.
따라서 iterations: Infinity라면 최댓값이 존재하지 않습니다.
애니메이션 종료 시점(onfinish)에 currentTime을 출력하면 1000이 나옵니다.
const span = document.getElementById('ani');
const animation = span.animate([
{ transform: 'scale(1)', },
{ transform: 'scale(0.5)', },
{ transform: 'scale(1)', },
],{
duration:1000,
delay:0,
iterations:1,
})
animation.onfinish = (()=> {console.log(animation.currentTime)}); //1000
playbackRate는 타임라인이 얼마나 빨리 진행되는지를 결정하지만
playbackRate를 2로 설정하여 두 배 빠르게 재생시킨다고 해서
타임라인이 1000ms -> 500ms가 되진 않습니다.
재생 속도를 어떻게 바꾸더라도 타임라인은 0ms -> 1000ms까지 존재합니다.
animation.playbackRate = 2;
animation.onfinish = (()=> {console.log(animation.currentTime)}); //여전히 1000
currentTime은 쓰기도 가능합니다.
이를 통해 랜덤하게 움직이는 두 개 이상의 애니메이션을 동기화 시킬 수도 있어요.
player2.currentTime = player.currentTime;
이 모든 기능을 이용해서 애니메이션을 만들어보았습니다.
mdn공식 홈페이지에 있는 예제에 이벤트 리스너를 추가하여
애니메이션의 상태를 변경하는 예제로 만들었습니다.
See the Pen waapi alice by seoulsaram (@seoulsaram) on CodePen.
1. 앨리스를 한 번 클릭하면 애니메이션이 멈춥니다. (pause())
2. 앨리스를 두 번 클릭하면 애니메이션이 역재생 됩니다. (reverse())
3. faster버튼을 누르면 애니메이션이 1.2배씩 빨라집니다. (playbakcRate를 직접 업뎃해도 되지만 updatePlaybackRate 함수를 사용해도 됩니다.)
4. slower버튼을 누르면 애니메이션이 1.2배씩 느려집니다.
CSS로만 애니메이션을 구현했다면 불가능하거나 훨씬 복잡했을 기능들을
WAAPI로는 아주 쉽게 구현할 수 있습니다.
만약 animation객체를 따로 저장하지 않았다면
Element.getAnimation() 메서드는 특정 요소에 적용된 모든 Animation객체를 배열로 가져옵니다.
이 함수를 사용하면 animate()호출 당시 변수에 Animation객체를 저장하지 않아도
이미 실행중인 애니메이션을 제어할 수 있습니다.
span.animate([...]);
span.animate([...]);
console.log(span.getAnimations()); // [Animation, Animation]
보시다시피 하나의 요소에 여러 애니메이션을 줄 수도 있습니다.
문서 전체의 애니메이션 조회하기
Document.getAnimations()메서드는 문서 전체에서 실행중인 모든 애니메이션을 가져옵니다.
전체 페이지 애니메이션을 일괄 중지하거나 디버깅 할 때 사용합니다.
const allAnimations = document.getAnimations();
allAnimations.forEach(anim => anim.pause());
여러 공들이 움직이는 애니메이션을 한 번에 멈추는 예제를 살펴봄으로써 getAnimations함수를 활용해볼까요?
See the Pen waapi balls by seoulsaram (@seoulsaram) on CodePen.
여러 엘리먼트가 날아다니고 있지만 document.getAnimations로 한 번에 군기를 잡을 수 있습니다.
💡 Good to know
스팩에서는 document 객체에 getAnimations()메서드를 둘 수 있도록 허용하고 있고, 최신 버전의 스펙에서는 이 메서드가 document에 직접 포함됩니다.
하지만 Crhome 52와 polyfill(v2.2.0기준)에서는 구 스펙을 따르고 있어, getAnimations()가 document가 아니라 document.timeline객체에 있습니다. 때문에 다음과 같이 예외처리를 해주는 것이 좋습니다.
var animations = document.getAnimations ?
document.getAnimations()
: document.timeline.getAnimations();
원하는 타이밍에 애니메이션 재생하기 — Animation 생성자
지금까지 본 animate()함수는 호출 즉시 애니메이션이 재생됩니다.
하지만 애니메이션 설정만 미리 해놓고 사용자 액션에 의해 애니메이션이 실행되게 하고싶으면
어떻게 하는게 좋을까요?
var effect = new KeyframeEffect(elem, keyframes, timings);
var player = new Animation(effect, document.timeline);
player.play(); // 원하는 타이밍에 재생
이 방식은 사용자 클릭 시 실행되는 애니메이션 같은 곳에서 매우 유용합니다.
사용자가 클릭하면 토끼가 나왔다 들어가는 애니메이션 예제를 통해 살펴봅시다.
See the Pen rabbit by seoulsaram (@seoulsaram) on CodePen.
마치며
단순히 반복되는 애니메이션을 구현할 때는 CSS가 가장 간단하고 효율적인 선택입니다.
하지만 사용자와의 상호작용, 조건 기반 동작, 데이터 변화에 따라 유연하게 반응해야 하는 애니메이션이라면 이야기가 달라집니다.
이런 상황에서는 WAAPI(Web Animations API) 가 매우 강력한 도구가 됩니다.
WAAPI의 장점은 명확합니다.
- CSS와 매우 유사한 문법을 사용해 러닝커브가 낮고,
- JS의 유연성과 제어력을 그대로 가져오며,
- 무엇보다 웹 표준이라는 점입니다.
물론 아직 일부 기능은 폴리필이 필요하고 브라우저 지원이 완벽하지는 않습니다.
하지만 앞으로 Motion Path 같은 고급 기능이 보편화되면,
SVG 기반의 복잡한 경로 애니메이션까지 표준 방식으로 다룰 수 있게 될 것입니다.
WAAPI는 지금도 충분히 실용적이며, 앞으로 더욱 기대되는 기술입니다.
참고
Using the Web Animations API - Web APIs | MDN
The Web Animations API opens the browser's animation engine to developers and manipulation by JavaScript. This API was designed to underlie implementations of both CSS Animations and CSS Transitions, and leaves the door open to future animation effects. It
developer.mozilla.org
Let's Talk about the Web Animations API
Let's Talk about the Web Animations API Jul 20, 2015Updated Jun 14, 2016 Let’s talk about the Web Animations API This is an introduction to a tutorial series on the Web Animations API coming to browsers. I've updated the series content in June 2016, as C
danielcwilson.com
'프론트엔드' 카테고리의 다른 글
| 유튜브는 왜 리액트를 쓰지 않았을까? — Web Components (1) | 2025.11.19 |
|---|---|
| 라이브러리 없이 재사용 가능한 컴포넌트 만들기 (feat. Web Components) (0) | 2025.11.19 |
| 생각지도 못한 <picture>태그의 활용 (0) | 2025.09.22 |
| 최대한 효율적인 이미지 로딩을 목표로 브라우저 일시키기 (0) | 2025.09.15 |
| 논리 픽셀, 물리 픽셀이 뭔지 아시나요? DPR 제대로 이해하기 (0) | 2025.08.20 |