[HTML5 Canvas] 03 캔버스 애니메이션

9rganizedChaos·2021년 5월 30일
1

HTML5_Canvas

목록 보기
3/6
post-thumbnail

✹ 해당 포스팅은 Youtube에 업로드된 1분코딩님의 <HTML5 Canvas 캔버스 라이브 강좌>의 필기입니다.
이 곳을 클릭하시면 유튜브에서 직접 강좌를 들으실 수 있습니다.
대부분의 샘플 코드는 해당 강좌에서 제공되는 코드임을 밝힙니다.

requestAnimationFrame

애니메이션을 공부할 때 수도 없이 보아왔던 requestAnimationFrame
MDN을 참고하면!

window.requestAnimationFrame()은 브라우저에게 수행하기를 원하는 애니메이션을 알리고 다음 리페인트가 진행되기 전에 해당 애니메이션을 업데이트하는 함수를 호출하게 합니다. 이 메소드는 리페인트 이전에 실행할 콜백을 인자로 받습니다.

그렇다면 리페인트란 무엇일까?

우리가 만든 웹문서를 브라우저에 렌더링할 때,
렌더트리를 배치하는 것이 리플로우, 그려내는 것이 리페인트와 각각 관련이 있다.
그리고 리플로우는 배치의 과정이 리페인트는 그리기의 과정이 다시 일어나는 과정을 뜻한다.

플로우 과정은...

배치과정은 돔트리를 구성한 후 렌더트리를 만드는 과정에서 여러 요소들의 위치와 크기 등을 계산하는 과정이다. 좀 더 자세히는 display, width, height, position 등을 꼐산하는 것이라 할 수 있겠다. 리플로우는 이벤트나 애니메이션이 발생해 UI가 변경될 때 발생한다.

페인트 과정은...

쉽게 말해 플로우에서 계산한 것을 실제로 유저의 눈에 보일 수 있도록 그려주는 것이 페인트이다. 색깔, 보터, 형태 등을 생각하면 더 쉽게 다가올 것이다. 이 때중요한 것은 플로우에서 계산한 것을 웹브라우저에서 나타내주는 과정이라는 점!

다시 requestAnimationFrame으로 돌아오면!

애니메이션에는 결국 지속적인 리페인트가 요구된다. 프레임을 반복적으로 그려주기 때문에!
문제는 리페인트는 리플로우의 계산이 다 마쳤을 때라야 의미가 있다.
리플로우의 계산이 우선순위가 밀리거나, 혹은 몹시 복잡한 계산으로 리페인트의 준비?가 오래걸릴 수 있다.
이럴때 무작정 프레임을 다시 그리는 명령을 내리면, 프레임이 유실되거나 뚝뚝 끊기는 느낌이 나게 된다.

결국 requestAnimationFrame이란, 리페인트를 위해 최적화가 완료될 때까지 기다려주는 메소드라고 요약할 수 있겠다.

requestAnimationFrame 샘플코드

      const canvas = document.querySelector('.canvas');
      const context = canvas.getContext('2d'); // 여기 까지는 걍 기본...!
      let xPos = 10;

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

        requestAnimationFrame(draw);
      }

      draw();

그러나 requestAnimationFrame 자체가 반복을 하는 애는 아니고, 재귀를 통해 그리기 함수를 호출해주어야 한다! 위 샘플 코드에서는 draw 함수! requestAnimationFrame의 첫 인자로 재귀로 실행시켜줄 함수를 넣어준 것을 확인할 수 있다.

SetInterval vs RequestAnimationFrame

"화면에 새로운 애니메이션을 업데이트할 준비가 될때마다 이 메소드를 호출하는것이 좋습니다. 이는 브라우저가 다음 리페인트를 수행하기전에 호출된 애니메이션 함수를 요청합니다. 콜백의 수는 보통 1초에 60회지만, 일반적으로 대부분의 브라우저에서는 W3C 권장사항에 따라 그 수가 디스플레이 주사율과 일치하게됩니다. 대부분의 최신 브라우저에서는 성능과 배터리 수명 향상을 위해 requestAnimationFrame() 호출은 백그라운드 탭이나 hidden <iframe>에서 실행이 중단됩니다."
<출처> MDN

만약 애니메이션의 반복 속도를 임의로 조작하고 싶다면?

SetInterval

사실 setInterval이 바로 requestAnimationFrame 등장 이전의 자바스크립트 애니메이션 구현에 주로 사용되는 함수였다고 한다! setInterval의 두 번째 인자에 반복 인터벌을 기입해 애니메이션 반복 속도를 조작할 수 있다!

      const canvas = document.querySelector('.canvas');
      const context = canvas.getContext('2d');
      let xPos = 10;

      function draw() {
        context.clearRect(0, 0, canvas.width, canvas.height);
        context.beginPath();
        context.arc(xPos, 150, 10, 0, Math.PI*2, false);
        context.fill();
        xPos += 10;
      }

      setInterval(draw, 500);

requestAnimationFrame

애니메이션 속도를 조작하는 setInterval이 간편해보이기도 하지만, requestAnimation을 사용하면서 애니메이션 구현 속도를 조작해야 하는 경우가 있다. (모바일 기기에서 배터리 절약에도 requestAnimationFrame이 유리하다. 그리고 결정적인 장점은 위에서 언급한 바와 같이 리페인트를 위한 최적화가 가능하다는 점! setInterval은 프레임이 유실되거나 할 수 있다!)
그럴 때는 아래와 같이 count 변수를 만들고 나머지 연산자를 이용한다!

      const canvas = document.querySelector('.canvas');
      const context = canvas.getContext('2d');
      let xPos = 10;
      let count = 0;

      function draw() {
        if (count % 30 === 0) {
          context.clearRect(0, 0, canvas.width, canvas.height);
          context.beginPath();
          context.arc(xPos, 150, 10, 0, Math.PI*2, false);
          context.fill();
          xPos += 5;
          // xPos = xPos + 3;
        }

        count++;
        requestAnimationFrame(draw);
      }

      draw();

그러나 위와 같은 경우 정확한 초수를 조작하기는 힘들지 않을까 하는 생각이 든다.
어디까지나 1/60초는 목표로 하는 속도이며, 찾다보니 "대부분의 브라우저에서는 W3C 권장사항에 따라 그 수가 디스플레이 주사율과 일치하게됩니다"라고 하는 걸보니...

cancelAnimationFrame

requstAnimationFrame을 취소하고 싶을 때는!
cancelAnimationFrame을 이용한다.

      canvas.addEventListener('click', () => {
        cancelAnimationFrame(timerId);
      });
profile
부정확한 정보나 잘못된 정보는 댓글로 알려주시면 빠르게 수정토록 하겠습니다, 감사합니다!

0개의 댓글