requestAnimationFrame API

송우든·2023년 11월 1일
0

Dev

목록 보기
14/18

최근에 svg와 canvas API 등 사용성을 익히고 있다. 이 과정에서 애니메이션을 만들어보며 requestAnimationFrame API에 대해 알게 되었다.

웹에서 애니메이션을 만드는 방법

CSS를 이용한 방법과 JS를 이용한 방법, 그리고 두 가지를 합쳐서 쓰는 방법이 있다.

보통 간단하고 규칙적인 애니메이션을 구현할 때에는 CSS를 이용해서 구현한다. 반대로 세밀한 작업과 복잡한 연산이 들어간 애니메이션을 구현해야한다면 자바스크립트를 사용하는 것이 좋다.

하지만 자바스크립트로 스타일 속성을 변화시키는 방법은 CSS보다 성능적으로 좋지 않다.

그렇기 때문에 애니메이션을 최적화하는 과정이 필요하고, requestAnimationFrameAPI를 통한 애니메이션 최적화가 필요하다. 애니메이션을 최적화하는 방법에는 여러가지가 있다. 하지만, 그 중에서도 requestAnimationFrameAPI를 이용한 애니메이션 최적화를 다루어보려고 한다.

requestAnimationFrame API가 무엇인가

MDN에는 위와 같이 정의되어 있다. 즉, 다음에 실행해야 하는 애니메이션을 콜백 함수로 전달하고 리페인트 전에 전달한 콜백함수를 실행하는 역할을 해준다. requestAnimationFrameAPI를 사용하면 브라우저 비활성화시 리소스 낭비 방지나 좀 더 부드러운 애니메이션을 만드는 등 이점이 있다.

기본 사용방법을 알아보자. requestAnimationFrame()을 이용하여 다음으로 실행해야 하는 애니메이션 프레임을 예약하는 방식으로 프레임 단위로 동작하기 때문에 별도의 반복 플래그가 필요 없다.

let raf; // requestAnimationFrame 객체를 담을 변수
const animate = () => {

	// raf 종료(중지) 조건
	if(조건) {
		cancelAnimationFrame(raf); // 애니메이션 취소
		return;
	}
	raf = requestAnimationFrame(animate); // 재귀 호출
};

requestAnimationFrame(animate);

animate()라는 함수는 디스플레이의 주사율에 따라 호출될 것이다. 일반적으로는 1초에 60번 호출되지만, requestAnimationFrameAPI는 디스플레이의 주사율에 따라 호출된다.(디스플레이의 주사율에 따르게 최적화되어 있다.)

또한, 아래와 같이 타이머를 이용해서 일정 시간동안 실행되는 애니메이션을 구현할 수 있다.

let startTime;

const animation = (tp) => {

	if(!startTime) startTime = tp; // 애니메이션 시작 시간
	let currentTime = tp - startTime;

	if(currentTime > 1000) {
		// 종료
		return;	
	} 

	requestAnimationFrame(animation);
};

requestAnimationFrame(animation);

애니메이션 만들기 실습

아래와 같이 간단하게 svg 예제를 만들어봤다. 별 이미지를 svg에 부착하였고, 별이 대각선 위로 이동하는 애니메이션을 구현해보려고 한다.

const NS_URI = "http://www.w3.org/2000/svg";
const section = document.createElement("section");
const svg = document.createElementNS(NS_URI, "svg");
const image = document.createElementNS(NS_URI, "image");

svg.setAttribute("viewBox", "0 0 700 1000");
svg.setAttribute("width", "100vw");
svg.setAttribute("height", "100vh");

image.setAttribute("id", "img-star");
image.setAttribute("href", "/assets/images/img-star.svg");
image.setAttribute("x", "-270");
image.setAttribute("y", "830");
image.setAttribute("width", "100");
image.setAttribute("height", "100");

svg.appendChild(image);
section.appendChild(svg);
document.querySelector("#app")?.appendChild(section);

위와 같이 코드를 작성하면 아래와 같은 이미지를 볼 수 있다.

그럼, 여기에 대각선 위로 이동하는 애니메이션 코드를 추가해보자. 아래와 같이 애니메이션이 동작해야하는 코드를 추가하고 종료조건인 경우에만 cancelAnimationFrame()을 통해 애니메이션을 취소해주면 된다.

let raf: number;
const animate = () => {
  const x = parseInt(image.getAttribute("x") ?? "0");
  const y = parseInt(image.getAttribute("y") ?? "0");

	// 애니메이션 종료 조건
  if (x >= 800) {
    cancelAnimationFrame(raf); // 애니메이션 취소
    return;
  }

  image.setAttribute("x", `${x + 7}`);
  image.setAttribute("y", `${y - 5}`);
  raf = requestAnimationFrame(animate); // 재귀 호출
};

마무리

이전에는 단순히 setInterval이나 setTimeout을 이용해서 1초마다 위치를 갱신하게 구현했었다. 이번 학습을 통해서 단순히 구현하는 것이 아닌 좀 더 좋은 방식을 찾아 학습하고 적용해보는 과정을 배울 수 있었다.

profile
개발 기록💻

0개의 댓글