관련지식
javascript, canvas

예전에 반드시 플래시를 사용해야만 했던 동적인 애니메이션을 이제는 canvas를 이용하여 처리할 수 있습니다. 여기선 간단한 기초적인 사용법을 보이겠습니다.

캔버스를 사용하기 위해선 HTML에 아래와 같은 태그가 반드시 1개 이상 사용되어야 합니다.

  1. <canvas id="canvas"></canvas>

그리고 이를 제어하기 위해 아래와 같이 자바스크립트를 사용할 것입니다.

  1. var canvas = document.getElementById("canvas");
  2. var ctx = canvas.getContext("2d");

만약 캔버스에서 3D 모델링을 하려면 getContext() 함수의 파라미터를 다르게 써야 합니다.

도형 그리기

캔버스에선 직사각형, 원 뿐 아니라 조금 번거롭더라도 복잡한 도형도 그려낼수 있습니다. 그중 가장 간단한 직사각형을 그리는 방법엔 아래와 같은 방법이 있습니다.

  • fillRect(x, y, width, height)
  • strokeRect(x, y, width, height)
  • clearRect(x, y, width, height)

fillRect() 함수를 이용해 그려보겠습니다.

  1. ctx.fillRect(0, 0, 50, 60);

가로 50px, 세로 60px 의 검은색 사각형이 보이실 것입니다.

참고 : https://developer.mozilla.org/ko/docs/Web/HTML/Canvas/Tutorial/Drawing_shapes

텍스트 그리기

도형을 그리듯이 텍스트도 그릴수 있습니다. 위 샘플에 이어서 아래 소스를 추가해봅시다.

  1. ctx.fillText('8', 25, 30);

하지만 화면에선 검은색 사격형 외엔 보실수가 없을 것입니다. 검은색 사각형을 그릴때 아무 색도 지정안했는데 검은색으로 그려졌듯이 현재 상태는 검은색을 사용하기 때문에 텍스트도 검은색으로 그려진 것입니다. 따라서 텍스트를 구분하기 위해 하얀색으로 지정해 보겠습니다.

  1. ctx.fillStyle = "#FFF"; //추가, 하얀색으로 지정
  2. ctx.fillText('8', 25, 30);

이번엔 글씨가 보이긴 하는데 너무 작네요. 폰트를 키워보겠습니다.

  1. ctx.font = '52px serif';
  2. ctx.fillStyle = "#FFF"; //추가, 하얀색으로 지정
  3. ctx.fillText('8', 25, 30);

커진것 같긴 한데 글씨 위치 선정이 매우 안좋습니다. 늘 그렇듯이 정렬이 필요합니다.

  1. ctx.font = '52px serif';
  2. ctx.fillStyle = "#FFF";
  3. ctx.textAlign = 'center';
  4. ctx.textBaseline = 'middle';
  5. ctx.fillText('8', 25, 10);

textAlign 속성은 흔히 사용하는 정렬 개념과 크게 차이가 없지만 textBaseline 속성은 문자의 글씨 쓰기 방식과 관련이 있기 때문에 API를 확인할 필요가 있습니다.

참고 : https://developer.mozilla.org/ko/docs/Drawing_text_using_a_canvas

애니메이션 적용

애니메이션 적용을 하기 전에 지금까지 만든 샘플에 아래 소스를 추가하겠습니다.

  1. ctx.fillStyle = "#FFF";
  2. ctx.fillRect(0, 29, 50, 1);

잘 따라왔다면 아래와 같은 형태가 보일겁니다.

좀 어설프지만 많이 보던 형태입니다. 탁구 경기에서 볼수 있는 점수판 같기도 하고 디지털 시계 디자인 같기도 합니다. 점수판이 바뀌는듯한 애니메이션을 넣어보겠습니다.

먼저 지금까지 만든것을 정리 좀 하겠습니다. 정리된 소스는 아래와 같습니다.

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1">
  6. <style>
  7. </style>
  8. </head>
  9. <body>
  10. <canvas id="canvas"></canvas>
  11. <script>
  12. var canvas = document.getElementById("canvas");
  13. var ctx = canvas.getContext("2d");
  14. function draw(num) {
  15. ctx.fillStyle = "#000";
  16. ctx.fillRect(0, 0, 50, 60);
  17. ctx.font = '52px serif';
  18. ctx.fillStyle = "#FFF";
  19. ctx.textAlign = 'center';
  20. ctx.textBaseline = 'middle';
  21. ctx.fillText(num, 25, 30);
  22. ctx.fillStyle = "#FFF";
  23. ctx.fillRect(0, 29, 50, 1);
  24. }
  25. draw(8);
  26. </script>
  27. </body>
  28. </html>

애니메이션은 1초후에 실행시킬 것입니다.

  1. setTimeout(next, 1000);
  2. function next() {
  3. }

캔버스에 그린 이미지의 윗부분만의 높이를 움직여 접히는듯한 효과를 줄 것입니다. 이미지 높이를 변경하기 위해선 transform() 함수를 사용해야 하는데 이 함수를 포함한 회전, 확대/축소등의 모든 함수는 포토샵과는 개념이 좀 다릅니다.

예를들어 포토샵에서 이미지의 특정 영역 크기를 조절하고 싶을땐 ‘영역선택 -> 변형’ 또는 ‘원하는 레이어 선택 -> 변형’ 을 하면 되는데 캔버스에서는 이미지나 특정 영역을 변형하는 형태가 아니라 캔버스를 변형하는 구조입니다. 용어가 좀 겹치네요. 크레파스로 그린 영역을 변형하는게 아니라 스케치북을 변형한다는 뜻입니다.

  1. var half = ctx.getImageData(0, 0, 50, 30); //이미지의 윗부분만 가져오기
  2. ctx.setTransform(1, 0, 0, 0.5, 0, 0); //캔버스 모양 변형

그런데 위 코드를 실행하면 변형되지 않습니다. 캔버스 모양을 변형한 후에 그위에 그림을 그려야만 변형된 모양이 보여집니다.

캔버스에서 이미지를 그릴땐 보통 drawImage() 함수를 사용하는데 여기선 그 방법을 직접 사용할 수가 없습니다. getImageData()로 가져온 데이터는 ImageData 객체로 drawImage()는 raw 데이터를 요구하기 때문입니다. ‘ImageData -> raw 데이터’ 변환을 위해 createImageBitmap() 함수를 사용할 수 있지만 호환성이 매우 안좋은 함수이기 때문에 임시 캔버스를 이용한 꼼수를 보여드리겠습니다.

  1. <canvas id="tmpCanvas" style="display:none"></canvas> <!--임시캔버스 추가-->
  1. var half = ctx.getImageData(0, 0, 50, 30); //이미지의 윗부분만 가져오기
  2. ctx.setTransform(1, 0, 0, 0.5, 0, 0); //캔버스 모양 변형
  3. tmpCtx.putImageData(half, 0, 0); //임시 캔버스에 이미지 윗부분 추가
  4. ctx.drawImage(tmpCanvas, 0, 0); //임시캔버스 내용을 가져오기

이제 실행하면 1초후에 세로 길이가 줄어든 이미지가 추가되는 것을 보실 수 있습니다.
이것을 연속으로 호출하면 애니메이션과 같은 효과를 줄 수 있습니다.(전체 소스는 밑에 있습니다.)

정리

순수한 canvas로만 무엇인가 만들려고 하면 손이 생가보다 많이 가는 편입니다. 때문에 다른 라이브러리를 이용해서 만드는게 훨씬 편할텐데 기본적인 개념잡을때는 기본 기능만큼 좋은것이 없죠. 몇가지 함수를 사용하며 어떻게 동작하는지 알아두면 좋을것 같습니다.

최종 샘플

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1">
  6. <style>
  7. </style>
  8. </head>
  9. <body>
  10. <canvas id="canvas"></canvas>
  11. <canvas id="tmpCanvas" style="display:none"></canvas>
  12. <script>
  13. var canvas = document.getElementById("canvas");
  14. var ctx = canvas.getContext("2d");
  15. var tmpCanvas = document.getElementById("tmpCanvas");
  16. var tmpCtx = tmpCanvas.getContext("2d");
  17. function draw(num) {
  18. ctx.fillStyle = "#000";
  19. ctx.fillRect(0, 0, 50, 60);
  20. ctx.font = '52px serif';
  21. ctx.fillStyle = "#FFF";
  22. ctx.textAlign = 'center';
  23. ctx.textBaseline = 'middle';
  24. ctx.fillText(num, 25, 30);
  25. ctx.fillStyle = "#FFF";
  26. ctx.fillRect(0, 29, 50, 1);
  27. }
  28. draw(8);
  29. setTimeout(next, 1000);
  30. function next() {
  31. var t = 1;
  32. var half = ctx.getImageData(0, 0, 50, 30); //이미지의 윗부분만 가져오기
  33. var ani = setInterval(function() {
  34. if(t <= -1) {
  35. clearInterval(ani);
  36. ctx.setTransform(1, 0, 0, 1, 0, 0);
  37. draw(9);
  38. return;
  39. }
  40. t -= 0.4;
  41. ctx.setTransform(1, 0, 0, t, 0, (1 - t) * 10 * 3); //캔버스 모양 변형
  42. tmpCtx.putImageData(half, 0, 0); //임시 캔버스에 이미지 윗부분 추가
  43. ctx.drawImage(tmpCanvas, 0, 0); //임시캔버스 내용을 가져오기
  44. }, 50);
  45. }
  46. </script>
  47. </body>
  48. </html>