index
index0. 애니메이션 구동 프로세스1. setTimeout과 setInterval의 한계2. requestAnimationFrame3. requestAnimationFrame를 사용할 수 없다면?
0. 애니메이션 구동 프로세스
방문한 웹페이지의 애니메이션이 살아 움직이는 것처럼 자연스러운가요? 아마 그 이유는 해당 애니메이션의 1초당 프레임 개수(fps)가 많기 때문일 것입니다. 최근의 기기들은 자연스러운 시각 효과를 위해 초당 60회의 화면을 그려내곤 하는데요. 이 경우 각 프레임에는 16ms 가량의 시간만 할당됩니다(1초/60 = 16.66ms).
그런데 브라우저가 이 모든 프레임을 구동하기 위해서는 실행 준비 시간이 필요합니다. 따라서 10ms 동안 모든 구동 준비(렌더링)를 마쳐야 합니다. 만약 이를 제한 시간(10ms) 내에 해내지 못하면, 우리가 종종 웹페이지에서 발견하곤 하는 애니메이션 버벅거림이 일어납니다.

1. setTimeout과 setInterval의 한계
안타깝게도,
setTimeout
과 setInterval
이 두 함수를 이용한다면 애니메이션이 생각보다 부드럽게 구동되지 않을 수 있습니다. 그 이유는 이 두 함수가 주어진 시간 내에 콜백만 하기 때문입니다. 즉 프레임 시작 시점이나 애니메이션의 렌더링 프로세스를 고려하지는 않습니다. 이 때문에 애니메이션이 버벅대는 문제가 발생합니다.또한,
setTimeout
과 setInterval
은 사용자가 페이지를 보지 않는 시점에서도 계속해서 실행돼, 배터리 수명이나 성능을 하락시킬 수 있습니다.
따라서 만일 부드럽고 끊김 없는 구동을 원한다면, 두 함수는 적절한 기능을 수행하지 않을 수 있습니다. 더불어 실행 시간이 길거나 타이밍이 좋지 않은 자바스크립트는 성능 문제를 쉽게 일으키곤 합니다. 안정적인 웹 애니메이션을 만들고 싶다면, 이 문제를 최소화할 방법을 찾아야 합니다.
2. requestAnimationFrame
이에 대안이 될 함수가 바로
requestAnimationFrame
입니다. 이 함수는 렌더링 파이프라인 내의 리페인트가 진행되기 전에 실행할 콜백을 인자로 받아 브라우저로 하여금 원하는 함수가 적절한 타이밍에 실행되게끔 합니다. 즉 프레임 개수가 많은 애니메이션이 자연스럽게 구동되기 위해, 각 프레임 시작 시점과 함수 구동 시점을 맞추도록 requestAnimationFrame
이 브라우저에게 명령을 내리는 것이죠. (렌더링 파이프라인의 시작 시점과 프레임 시작 시점을 맞추는 기능을 하는 겁니다!)그럼, 동그란 원이 왼쪽에서 오른쪽으로 빠르게 이동하는 간단한 애니메이션을 구현해보겠습니다.
먼저 동그란 원을 만들어주어야 합니다.
<!DOCTYPE html> <html> <head> <title>moving circle</title> <style type="text/css"> .circle{ position: relative; border: 1px solid yellow; border-radius:50px; background: yellow; width:100px; height:100px; } </style> </head> <body> <div class="circle"></div>
이렇게 노란색 동그라미를 만들어 주셨다면 이제는, go()라는 함수를 만들어보겠습니다.
var touch = 0; var a = document.querySelector(".circle"); var fast = 0; function go() { a.style.left = ((count += 5) % 700) + "px"; // 지속적으로 style태그에 속도를 낼 수 있게 count를 추가해준다. touch = requestAnimationFrame(go); } requestAnimationFrame(go);
노란색 동그라미가 쉼 없이 왼쪽에서 오른쪽으로 움직이는 애니메이션이 만들어졌습니다.
만약 게임을 만들 듯 원을 클릭하면 멈추는 이벤트를 만들고 싶으신가요? 그렇다면
requestAnimationFrame
을 취소해주시면 됩니다! 그 방법은 cancleAnimationFrame
명령을 브라우저에 내려주시는 겁니다. 아래와 같이, 조건을 추가해주세요.document.querySelector(".circle").addEventListener("click", function () { if(touch) { window.cancelAnimationFrame(touch); touch = null; } else { go(); } });
이렇게 간단히, 움직이는 애니메이션을 끊김 없이 만들고 또 원한다면 멈추게도 하는 작업을 완료했습니다. 번외로 만들고 확인해보시면 아시겠지만, 애니메이션이 움직이는 상태에서 다른 창으로 이동했다가 다시 돌아왔을 때, 잠시 멈췄던 애니메이션이 다시 움직이는 것을 확인하실 수 있을 겁니다. 이렇게
requestAnimationFrame
은 끊김도 없고, 배터리도 아껴주는 좋은 함수입니다 :)3. requestAnimationFrame를 사용할 수 없다면?
혹시
requestAnimationFrame
함수를 이용하기 어려운가요?requestAnimationFrame
함수는 앞서 배운 setTimeout
함수와 setInterval
함수에 비해 최근에 만들어진 함수입니다. 따라서 이 함수를 지원하지 않는 브라우저에서는 브라우저 이용현황 보러가기 이용이 어려울 수 있습니다. 또한 몇몇 브라우저는 requestAnimationFrame
이 아닌 다른 이름으로 함수를 정의해 놓아 사용자에게 혼동을 일으키기도 합니다.window.requestAnimationFrame = (function(callback) { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60); }; })();
다행히, 이를 극복할 방법이 있습니다. 상단의 코드를 작성해 넣으시면 일괄적으로
requestAnimationFrame
이라는 이름으로 해당 기능을 사용할 수 있습니다.