앞장에서 캔버스는 그림을 그린다고 생각을 해야 한다고 했었는데 우리가 아는 일반적으로 애니메이션은 그림을 여러장그려서 넘기면 애니메이션이 된다.
캔버스의 애니메이션도 동일하다. 캔버스에서 애니메이션을 그리기 위해서는 기존에 존재하던 그림을 지우고 새로운 위치에 그림을 그리는것을 반복하면된다.
애니메이션을 그리기 위해서는 2가지 방식이 존재한다 requestionAnimationFrame를 사용하는 방법과 setInterval을 사용하는 방법이 있다.
예전에는 setInterval
을 사용해서 애니메이션 처리를 많이 했는데 지금은 requestionAnimationFrame
를 대부분 사용한다고 한다. 이유를 알기위해서 이를 비교해보았다.
requestionAnimationFrame | setInterval |
---|---|
리페인트(repaint) 주기에 맞춰 애니메이션을 업데이트하도록 설계 | 일정한 간격으로 반복적으로 함수를 호출 |
브라우저가 비활성화되거나 다른 탭으로 전환되면 애니메이션이 일시 중지되어 불필요한 리소스 소모를 방지 | 브라우저나 시스템 상황과 상관없이 설정된 시간 간격대로 애니메이션을 업데이트 |
디스플레이 주사율(refresh rate)에 맞춰 애니메이션을 렌더링하므로 부드러운 애니메이션을 제공 | 시간 간격이 너무 짧으면 과도한 CPU 사용으로 성능 이슈가 발생 |
단일 스레드 기반으로 작동하므로 다른 작업과 충돌할 가능성이 낮음 | 간격 시간이 정확하지 않아 프레임 드롭(frame drop)이 발생 |
비교글에 따르면 setInterval은 정확성과 성능 문제가 있으므로 requestionAnimationFrame를 사용하는게 더 효율적으로 보인다.
추가로 requestionAnimationFrame의 업데이트는 1/60초 속도를 목표로 반복을 한다고 한다.
이번 실습에서는 requestionAnimationFrame와 serInterval 두가지를 이용해서 애니메이션을 구현하는것을 진행하였다.
const canvas = document.querySelector(".canvas");
const context = canvas.getContext("2d");
let xPos = 10;
// draw 함수는 1프레임이라고 생각하면 됨
const draw = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(xPos, 150, 10, 0, Math.PI * 2);
context.fill();
xPos += 1;
if (xPos > canvas.width) xPos = 10;
// 드로우가 계속 반복됨
requestAnimationFrame(draw);
};
draw();
const canvas = document.querySelector(".canvas");
const context = canvas.getContext("2d");
let xPos = 10;
let count = 0;
// draw 함수는 1프레임이라고 생각하면 됨
const draw = () => {
// 나누기 연산자를 이용한 속도 조절
if (count % 10 === 0) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(xPos, 150, 10, 0, Math.PI * 2);
context.fill();
xPos += 1;
if (xPos > canvas.width) xPos = 10;
}
// 드로우가 계속 반복됨
count++;
requestAnimationFrame(draw);
};
draw();
속도조절이 굉장히 쉽지만 그리기만 하기 때문에 버벅이는 이슈가 생김
const canvas = document.querySelector(".canvas");
const context = canvas.getContext("2d");
let xPos = 10;
// draw 함수는 1프레임이라고 생각하면 됨
const draw = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(xPos, 150, 10, 0, Math.PI * 2);
context.fill();
xPos += 1;
if (xPos > canvas.width) xPos = 10;
};
// 드로우가 계속 반복됨
setInterval(draw, 10);
애니메이션을 사용하다보면 화면을 클릭하거나 특정위치를 클릭하거나 할때 애니메이션을 멈추고 싶은 경우가 존재하는데 이때를 위해서 애니메이션을 멈추는 방법을 2가지 소개한다.
requestionAnimationFrame을 멈추기위해서는 2가지 방법이 존재한다. 추가적으로 캔버스 화면내를 클릭하여 멈추는 방법도 소개한다.
const canvas = document.querySelector(".canvas");
const context = canvas.getContext("2d");
let xPos = 10;
// draw 함수는 1프레임이라고 생각하면 됨
const draw = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(xPos, 150, 10, 0, Math.PI * 2, false);
context.fill();
xPos += 1;
// 애니메이션 멈추는 방법
if (xPos >= canvas.width - 10) {
return;
}
// 드로우가 계속 반복됨
requestAnimationFrame(draw);
};
draw();
const canvas = document.querySelector(".canvas");
const context = canvas.getContext("2d");
let xPos = 10;
let timerId;
// draw 함수는 1프레임이라고 생각하면 됨
const draw = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(xPos, 150, 10, 0, Math.PI * 2, false);
context.fill();
xPos += 1;
// 드로우가 계속 반복됨
timerId = requestAnimationFrame(draw);
// 애니메이션 취소하기
if (xPos >= canvas.width - 10) {
cancelAnimationFrame(timerId);
}
};
draw();
const canvas = document.querySelector(".canvas");
const context = canvas.getContext("2d");
let xPos = 10;
let timerId;
// draw 함수는 1프레임이라고 생각하면 됨
const draw = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(xPos, 150, 10, 0, Math.PI * 2, false);
context.fill();
xPos += 1;
// 애니메이션 반복 재시작
if (xPos > canvas.width) xPos = 10;
// 드로우가 계속 반복됨
timerId = requestAnimationFrame(draw);
};
draw();
// 클릭하여 애니메이션 멈추기
canvas.addEventListener("click", () => {
cancelAnimationFrame(timerId);
});
setInterval의 경우 clearInterval을 사용하여 애니메이션을 멈추면 된다.
const canvas = document.querySelector(".canvas");
const context = canvas.getContext("2d");
let xPos = 10;
let timerId;
// draw 함수는 1프레임이라고 생각하면 됨
const draw = () => {
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.arc(xPos, 150, 10, 0, Math.PI * 2);
context.fill();
xPos += 1;
if (xPos > canvas.width) xPos = 10;
};
// 드로우가 계속 반복됨
timerId = setInterval(draw, 10);
canvas.addEventListener("click", () => {
clearInterval(timerId);
});