캔버스 애니메이션

송기영개발잘하고싶다·2024년 5월 26일
0

캔버스

목록 보기
3/8
post-thumbnail

개요

앞장에서 캔버스는 그림을 그린다고 생각을 해야 한다고 했었는데 우리가 아는 일반적으로 애니메이션은 그림을 여러장그려서 넘기면 애니메이션이 된다.

캔버스의 애니메이션도 동일하다. 캔버스에서 애니메이션을 그리기 위해서는 기존에 존재하던 그림을 지우고 새로운 위치에 그림을 그리는것을 반복하면된다.

애니메이션을 그리기 위해서는 2가지 방식이 존재한다 requestionAnimationFrame를 사용하는 방법과 setInterval을 사용하는 방법이 있다.

예전에는 setInterval을 사용해서 애니메이션 처리를 많이 했는데 지금은 requestionAnimationFrame를 대부분 사용한다고 한다. 이유를 알기위해서 이를 비교해보았다.

requestionAnimationFrame과 setInterval 비교

requestionAnimationFramesetInterval
리페인트(repaint) 주기에 맞춰 애니메이션을 업데이트하도록 설계일정한 간격으로 반복적으로 함수를 호출
브라우저가 비활성화되거나 다른 탭으로 전환되면 애니메이션이 일시 중지되어 불필요한 리소스 소모를 방지브라우저나 시스템 상황과 상관없이 설정된 시간 간격대로 애니메이션을 업데이트
디스플레이 주사율(refresh rate)에 맞춰 애니메이션을 렌더링하므로 부드러운 애니메이션을 제공시간 간격이 너무 짧으면 과도한 CPU 사용으로 성능 이슈가 발생
단일 스레드 기반으로 작동하므로 다른 작업과 충돌할 가능성이 낮음간격 시간이 정확하지 않아 프레임 드롭(frame drop)이 발생

비교글에 따르면 setInterval은 정확성과 성능 문제가 있으므로 requestionAnimationFrame를 사용하는게 더 효율적으로 보인다.

추가로 requestionAnimationFrame의 업데이트는 1/60초 속도를 목표로 반복을 한다고 한다.

실습

이번 실습에서는 requestionAnimationFrame와 serInterval 두가지를 이용해서 애니메이션을 구현하는것을 진행하였다.

requestAnimationFrame

      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();

setInterval

속도조절이 굉장히 쉽지만 그리기만 하기 때문에 버벅이는 이슈가 생김

      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);

결과

애니메이션 멈추기(2024. 06. 12 추가)

애니메이션을 사용하다보면 화면을 클릭하거나 특정위치를 클릭하거나 할때 애니메이션을 멈추고 싶은 경우가 존재하는데 이때를 위해서 애니메이션을 멈추는 방법을 2가지 소개한다.

requestionAnimationFrame

requestionAnimationFrame을 멈추기위해서는 2가지 방법이 존재한다. 추가적으로 캔버스 화면내를 클릭하여 멈추는 방법도 소개한다.

  • 멈추기
    첫번째로는 draw함수에서 호출에서 return값을 아무것도 주지 않게되면 멈출 수 있다.
 	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();
  • 취소하기
    두번째로는 cancelAnimationFrame을 사용하는 방법이다.
      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();
  1. 클릭하여 멈추기
	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

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);
      	});

결과

profile
업무하면서 쌓인 노하우를 정리하는 블로그🚀 풀스택 개발자를 지향하고 있습니다👻

0개의 댓글