
애니메이션을 구현하는 방법에는 어떤 것들이 있을까요 ?!
css를 이용하여 구현한다.
requestAnimationFrame 전역 메소드를 활용하여 구현한다.
setTimeout, setInterval 메소드를 활용하여 구현한다.
우선 이번 어플리케이션은 프로그램 명세에 맞게 requestAnimationFrame 으로 애니메이션을 구현해보았습니다. 하지만 문득 이런 고민 들지않았나요?
다른 방식으로 짜면 구린건가..?
그래서 이번 절에선 세 개간 트레이드 오프를 간단하게나마 비교해보려고 합니다.
브라우저의 렌더링 - layout(reflow) : 렌더 트리를 실제 뷰포트에 배치하는 과정
브라우저의 렌더링 - paint(repaint) : 실제 브라우저 화면에 그리는 과정
브라우저의 렌더링 - composite : 페인트 과정에서 분리하여 작업해놓았던 레이어들을 합성하는 과정
event loop가 관리하는 자료구조와 동작방식
CSS 기반 애니메이션과 기본 지원되는 웹 애니메이션은 일반적으로
컴포지터 스레드라고 불리는 스레드에서 처리됩니다. 이것은 스타일 지정, 레이아웃, 페인트 및 자바스크립트가 실행되는 브라우저의메인 스레드와는 다릅니다.
메인스레드의 작업이 커진다면, JS Animation은 밀리게 되겠죠 ?! 하지만 CSS Animation 이라면 별개의 스레드에서 실행되므로 밀리지 않게 될것입니다.
애니메이션이 페인트 및/또는 레이아웃을 트리거하는 경우, 작업을 수행하기 위해
메인 스레드가 필요합니다. 이는 CSS 및 자바스크립트 기반 애니메이션에 모두 적용되며, 레이아웃 또는 페인트의 오버헤드는 CSS 또는 자바스크립트 실행과 연관된 모든 작업에 악영향을 미치고, 해결 불가능한 문제를 유발할 수 있습니다.
보통 많이 들었을테지만, reflow와 repaint의 비용은 굉장히 크다고 소문이 나있죠. 이를 트리거 하는 애니메이션 속성들이 있을텐데 이를 피하는 것도 좋겠네요. 그게 뭔지 궁금하다면?
그래서 일단 CSS Animation의 장점은 제가 생각하기엔
개발에 걸리는 시간과 노력이 비교적 덜 들어간다. (덜 복잡하다)
관심사가 분리되어 유지보수에 편할것 같아요.
메인스레드와 분리된 스레드에서 동작하기 때문에, 수행 시간을 보장받을 수 있을 것 같아요.
단점은 이럴거 같아요.
CSS Anmation과 비교하여 생각해본다면 다음과 같은 문제점이 있을 것 같아요
관심사가 분리되지 않아 유지보수가 힘들다.
메인스레드에서 동작하기 때문에 수행 시간을 보장받지 못할 수 있다.
그렇기 때문에 모든 애니메이션을 JS로 구현하면 좋지 않을 것 같아요.
하지만 애니메이션을 구현할 때, 애니메이션 도중에 세밀한 제어가 필요한 경우라면 이 메소드를 활용해볼 수 있을 것 같아요.
requestAnimationFrame vs setTimeout 으로 애니메이션 구현하기결론부터 말씀드리자면, rAF로 애니메이션을 구현하는 경우 그 애니메이션은 부드럽습니다. 하지만 setTimeout으로 구현하는 경우 애니메이션은 부드럽지 않습니다. 왜 일까요?
박스가 돌아가는 애니메이션을 rAF를 함수로 구현해보겠습니다.
const box = document.querySelector(".box");
const rotateAnimation = (progress, start, node, during) => {
if (progress >= during) {
return;
}
node.style.transform = `rotate(${progress / 10}deg)`;
requestAnimationFrame((timestamp) =>
rotateAnimation(timestamp - start, start, node, during)
);
};
requestAnimationFrame((timestamp) => rotateAnimation(0, timestamp, box, 5000));
함수가 어떤 명세를 따르는지 궁금하다면 ? 요기를 찾아보세요
박스가 돌아가는 애니메이션을 setTimeout으로 구현해보겠습니다. 재귀 방식을 사용하여 애니메이션을 구현해보았습니다.
const box = document.querySelector(".box");
function setTimer(timestamp, start) {
const progress = timestamp - start;
setTimeout(() => {
box.style.transform = `rotate(${progress / 10}deg)`;
setTimer(new Date().getTime(), start);
}, 16);
}
setTimer(new Date().getTime(), 0);
구현은 위와 같이 할 수 있어요. 지금은 당연히 두 방식 다 부드럽게 애니메이션이 보일거에요. 그런데 왜 rAF Animation는 부드럽고, setTimeout Animation은 부드럽지 않다고 하는 걸까요?
task queue에 애니메이션 콜백만 있을까요?1프레임 당 한번의 호출을 보장하는가
모니터 주사율이 60hz의 경우 화면 갱신 속도는 60fps이고, 이 때 1프레임은 16ms 정도 나와야한다.
rAF의 경우 1프레임당 호출이 보장됩니다. 이 때 렌더링 파이프라인에 진입했을 때 부터 다음 파이프라인에 진입하는 그 사이를 1프레임이라하구요.
rAF는 Animation frames라는 브라우저 렌더링과 관련된 task를 별도로 처리하는 queue를 통해 실행시점을 보증할 수 있습니다.
setTimeout의 경우 이벤트 루프가 관리하는 taskqueue 내부의 다른 task에 의해 지연될 수 있어 끊길 수 있을 것 같아요
setTimeout은 task queue에 올라가 동작하고, rAF는 렌더링 파이프라인과 붙어 동작하는 것을 확인할 수 있다. 이런 구조 상 rAF는 무조건 1프레임 당 1번의 호출이 보장되고 setTimeout은 지연되어 2프레임 당 1번, 또는 3프레임 당 1번 호출될 가능성도 있다.
정리하자면, 단순한 애니메이션의 경우 (one-shot이자, 세부 제어가 필요없는 경우)라면 CSS Animation으로 작성하면 될 것 같아요. 그 외 복잡한 애니메이션이라면 JS Animation이 방안이 될 수 있을것이고, requestAnimationFrame 메소드를 통해 확실한 애니메이션을 표현해내면 되겠군요!
정리 너무 깔끔하네요~ 많은 도움이 되었어요 :)