본문 바로가기

인터넷과 유틸/HTML5

HTML5 - Canvas API 캔버스 API를 활용해 가장 기본적인 그림 그리기와 애니메이션 효과를 구현

캔버스 API의 현재
HTML5 캔버스 API를 활용하면 기존의 플래시가 갖고 있던 강점인 화려한 애니메이션과 인터랙티브한 효과를 낼 수 있다. 캔버스 API는 모바일 브라우저를 포함한 최신 브라우저에서 모두 지원하고 있지만, 국내 점유율이 높은 IE8(Internet Explorer 8) 이하에서는 지원하지 않아 아직 국내 웹에서는 활성화되진 않았다. 하지만 최근 캔버스 API를 지원하는 크롬이나 파이어폭스와 같은 브라우저의 점유율이 점점 높아지고 있는 반면, 캔버스 API를 지원하지 않는 구 버전 IE의 점유율은 낮아지고 있는 추세이다. 이런 추세가 계속 된다면 곧 국내 웹에서도 캔버스 API를 활용한 화려한 웹사이트들이 많이 등장할 것이다.

현재 해외에서는 캔버스 API를 이용한 사이트들이 많이 활성화돼 있으며, 관련된 자바스크립트 라이브러리들도 활발히 개발되고 있다. 특히 게임 분야에서 캔버스 API가 주목받으며 최근 캔버스 위에 3D 애니메이션을 넣을 수 있는 기술인 WebGL을 활용한 게임들이 급격히 증가하고 있다.



<화면 1> 캔버스 API로 구현된 e북

WebGL
WebGL은 Web-based Graphics Library의 줄임말로 캔버스 API로 3D 애니메이션을 구현할 수 있는 라이브러리다. WebGL은 아직 개발 중인 표준이라 완벽히 지원하는 브라우저는 없다. IE를 제외한 크롬, 파이어폭스, 사파리, 오페라에서 WebGL을 일부 지원하며 WebGL을 이용한 다양한 웹 애플리케이션들이 개발되고 있다. 대표적인 예로 사람의 몸을 해부학적으로 관찰할 수 있는 웹 애플리케이션인 구글 바디가 있다.



<화면 2> 사람의 몸을 3D로 해부해 볼 수 잇는 구글 바디

다양한 WebGL 작품들을 보려면 Mr.doob 웹사이트에 방문해 보는 것이 좋다. Mr.doob은 플래시를 활용한 창의적이고 멋진 작품들을 웹사이트에 올렸으나, 브라우저에서 HTML5에 대한 지원이 강화되며 최근 HTML5 기술을 이용한 작품들을 올리기 시작했다.



<화면 3> 3D 블록을 쌓을 수 있는 Mr.doob의 WebGL 작품

이 외에도 Chrome WebGL Experiments에서 많은 WebGL 작품을 감상할 수 있다. 이 사이트는 Chrome Experiments 사이트에 올라온 작품들 중 WebGL 기반의 작품들을 모아 놓은 곳이다. 이 사이트를 방문하면 세계 각국의 개발자들이 올린 다양한 작품들을 감상할 수 있다.

캔버스 API는 어디에 활용할 수 있을까?
앞서 캔버스 API가 게임 개발에 활용된다고 소개했다. 게임 외에 어떤 분야에서 캔버스 API가 활용될 수 있을까? 단순히 시선을 끌 수 있는 현란한 효과가 목적이라면 무언가 아쉬울 것이다.

먼저 캔버스 API를 이용하면 차트, 그래프에 동적인 시각 효과를 줄 수 있다. 기존에는 화면에 대각선 하나를 그리는 것도 힘들었던 것과 달리, 캔버스 API는 어떤 형태라도 그릴 수 있다. 주로 플래시를 이용하던 동적인 차트나 그래프 등을 이제 캔버스 API로 플러그인 없이 그릴 수 있다. 현재 HTML5를 이용해 차트를 그릴 수 있는 자바스크립트 라이브러리에는 RGraph와 AwesomeChartJS 등이 있다.



<화면 4> AwesomeChartJS를 이용해 만든 그래프들

또한 캔버스 API는 배너 광고에도 사용될 수 있다. 이 또한 기존에 플래시가 사용됐지만, 브라우저에서 캔버스 API에 대한 지원이 늘어나면 캔버스 API로 제작된 광고들이 활성화될 것이다. 캔버스 API를 활용하면 포토샵과 같은 그래픽 툴을 웹에서 제공하는 것도 가능해진다. 아직 포토샵이나 일러스트레이터 등의 데스크탑용 애플리케이션 만큼의 기능을 갖추진 못했지만 꽤 다양한 기능을 지원하는 그래픽 툴이 이미 개발돼 있다.



<화면 5> 캔버스 API로 제작된 그래픽 툴인 SketchPad

이 밖에도 에뮬레이터, 시뮬레이터 등을 개발하는 데 활용될 수 있으며, 웹에서 구동할 수 있는 원격제어 애플리케이션을 만드는 데에도 이용할 수 있다. 캔버스 API는 다양한 분야에 응용 가능해 캔버스 API가 발전할 수록 활용도는 무궁무진해 지고 있다.

캔버스 API 사용하기
이제 캔버스 API를 이용하여 캔버스 위에 선을 긋고 원을 그려보자.

좌표계

캔버스 API를 사용하기 전에 캔버스의 좌표계에 대해 살펴보자.



<그림 1> HTML5 캔버스의 좌표계

<그림 1> 캔버스로 점(0, 0)의 위치는 좌측 상단이며, 우측으로 갈수록 x값이 증가하고 아래로 갈수록 y값이 증가한다. 일반적으로 아래로 갈수록 좌표 값이 감소하는 데카르트 좌표계에 익숙하겠지만 캔버스 API에서는 증가 방향이 반대임에 주의해야 한다.

선 그리기

그림을 그리는 데에 가장 기본인 선 그리기를 우선 살펴보자. 캔버스 API에 무언가를 그리려고 한다면 먼저 그림을 그릴 공간을 나타내는 <canvas> 엘리먼트가 필요하다.

<canvas width=”500” height=”400” id=”myCanvas” style=”border:1px solid”>
< /canvas>

위 코드로 너비 500px, 높이 400px인 캔버스가 생성되며, 캔버스 영역을 쉽게 확인할 수 있도록 1px의 경계를 추가했다. 

캔버스 안에 선을 그리는 자바스크립트 코드를 단계별로 살펴보자. 먼저 <canvas> 엘리먼트에 접근해 캔버스 객체를 생성하고 그림을 그리는 데 필요한 API를 제공하는 context 객체를 생성해야 한다. <리스트 1>에서 context 객체를 생성하는 예제이다.

<리스트 1> context 객체 생성
// 먼저 <canvas> 엘리먼트의 id를 이용해 캔버스 DOM 객체 생성
var canvas = document.getElementById('myCanvas'),
// 캔버스 DOM 객체를 이용해 context 객체 생성 
context = canvas.getContext('2d');

context.beginPath();

<리스트 1>에서 getContext() 함수의 매개변수로 2차원 그림을 그릴 수 있는 ‘2d’를 사용했다. getContext() 함수의 매개변수로 2d 외에 ‘webGL’도 사용할 수 있지만 아직 이를 지원하는 브라우저는 없다.

선을 그리기 전에는 반드시 선 그리기를 시작한다는 의미의 beginPath() 함수를 호출해야 한다. beginPath() 함수를 먼저 호출하지 않고 선을 그려도 캔버스에 선이 그려지지만 beginPaht() 함수는 기존에 그려진 것을 초기화한다. 그러므로 잠재적인 문제 발생을 막기 위해 그림 그리기 전에 먼저 실행하는 것이 좋다. 

이제 실제로 선을 긋는 코드를 살펴보자. 직선을 그리는 과정은 사람이 손으로 선을 긋는 것과 비슷하다. <리스트 2>의 함수들은 먼저 선을 그을 시작점으로 펜을 들어 옮기고 원하는 곳까지 선을 긋는 동작과 같다.

<리스트 2> 직선 그리기 예제
// 선 긋기를 시작할 점을 지정. 각 좌표는 상대 좌표가 아닌 절대 좌표.
context.moveTo(100, 50);
// 선 긋기를 끝낼 마지막 점을 지정
context.lineTo(400, 350);
// stroke() 함수를 이용해 선이 캔버스에 나타나도록 함
context.stroke();


<리스트 2>를 브라우저에서 실행하면, <화면 6>처럼 캔버스에 대각선이 그어진 것을 확인할 수 있다.



<화면 6> 캔버스 위에 점(100, 50)에서 점(400, 350)까지 그어진 대각선

곡선 그리기

곡선은 quadraticCurveTo() 함수로 그릴 수 있다. quadratic은 ‘2차’란 뜻으로, 2차 베지어 곡선을 그릴 때에 사용하는 함수이다. 이 함수의 기본 형식은 다음과 같다.

context.quadraticCurveTo(cpx, cpy, x, y)

quadraticCurveTo() 함수를 호출하면 시작점에서 점(x, y)으로 이어진 곡선이 그려진다. 점(cpx, cpy)를 기준으로 곡선이 그려지므로, 해당 점의 위치를 변경하면 다양한 형태의 곡선을 그릴 수 있다.

선 그리기 예제인 <리스트 1>에 quadraticCurveTo()를 사용해 곡선을 그려보자.

<리스트 3>은 <리스트 2>에 사용한 lineTo() 함수만 quadratic CurveTo() 함수로 교체했다. <리스트 2>를 실행하면 <화면 7>처럼 좌측 하단으로 볼록한 곡선이 그려진다.

<리스트 3> 곡선 그리기 예제
var canvas = document.getElementById('myCanvas'),
context = canvas.getContext('2d');

context.beginPath();
context.moveTo(100, 50);
context.quadraticCurveTo(100, 350, 400, 350);
context.stroke();


<그림 2> 2차 베지어 곡선



<화면 7> 곡선 그리기 예제인 <리스트 3>의 결과

원 그리기

원은 arc() 함수로 그릴 수 있다. arc() 함수는 원을 그리는 함수라기보단 정확하게는 원호를 그리는 함수다. 원호뿐만 아니라 반원, 부채꼴과 같은 그림을 그릴 수 있다. arc() 함수의 기본 형식은 다음과 같다.

context.arc(x, y, radius, startAngle, endAngle[, anticlockwise])

앞의 두 매개변수 x와 y는 그려질 호의 중심점의 위치이며, radius는 원호의 반지름이다. startAngle은 호 그리기를 시작하는 각도이며, endAngle은 호 그리기를 종료하는 각도로 라디안 값으로 넘겨줘야 한다. 마지막으로 anticlockwise는 호를 그릴 방향을 결정하는 불리언(Boolean) 값이다. 매개변수 이름이 anticlockwise이므로 true를 넘기면 반시계 방향, false를 넘기면 시계 방향이 된다.

라디안

라디안은 각도를 표시하는 표준 단위로 많은 수학 분야에서 이용되고 있다. 캔버스 API를 이용해 그림을 그릴 때 라디안을 반드시 기억해야 한다. 1라디안은 원호의 길이가 반지름의 길이와 같아질 때의 각도를 의미한다.

라디안 = 각도 × π ÷ 180

위의 식을 이용하면 라디안 값을 쉽게 구할 수 있다. 예를 들어 90도는 1/2 라디안, 180도는 1라디안, 360도는 2라디안이다.

그럼 라디안을 구할 수 있는 arc() 함수를 이용해 원을 그려보자.

<리스트 4>는 중심점(200, 200)을 기준으로 반지름은 100px인 원을 그린다. startAngle과 endAngle 값을 변경하면 원 대신 원호를 그릴 수 있다.

<리스트 4> 원 그리기 코드
var canvas = document.getElementById('myCanvas'),
context = canvas.getContext('2d');

context.beginPath(); 
context.arc(200, 200, 100, 0, Math.PI*2, true); 
context.stroke();



<화면 8> 원 그리기 예제인 <리스트 4>의 결과

애니메이션 효과 넣기
캔버스 API에서 가장 기본인 선과 원 그리기 방법을 살펴봤다. 이 외에도 캔버스 API를 잘 활용하기 위해서 익혀야 하는 것들이 많지만, 지면상 모두 다룰 수 없으므로 그림 그리기 API에 대한 설명은 이 정도로 마친다. 지금부터는 더 재미있는 애니메이션 효과를 넣는 방법을 살펴보자. 

캔버스 API에서 제공하는 애니메이션 효과를 사용하기 위해서는 먼저 setInterval() 함수를 알아야 한다. 다음은 setInterval() 함수의 기본형이다.

var intervalID = window.setInterval(code, delay);

setInterval() 함수의 첫 번째 매개변수는 반복적으로 실행할 코드이며, 두 번째 매개변수는 밀리세컨드 단위의 실행시간 주기이다. 반환 값은 intervalID로 정수형이다. 이 값은 clear Interval() 함수를 이용해 반복 실행을 멈추는 데 사용한다.

이제 setInterval()를 사용해 애니메이션 효과를 넣어보자. 만들어 볼 애니메이션은 원의 크기가 커졌다 작아졌다를 반복하는 단순한 애니메이션이다. 동적으로 원의 반지름 값을 변환시키면 원의 크기가 변하는 효과를 낼 수 있다. <리스트 5>와 <리스트 6>은 해당 애니메이션의 코드이며, 결과는 http://bit.ly/vvvK03에서 볼 수 있다.

<리스트 5> 원을 그리는 예제
var canvas = document.getElementById('myCanvas'),
context = canvas.getContext('2d');

function drawCircle(rad) {
context.beginPath();
context.arc(200, 200, rad, 0, Math.PI*2, true); 
context.stroke();
}


먼저 context 객체를 생성한 후, 원을 그리는 drawCircle() 함수를 생성했다. drawCircle() 함수는 반지름을 매개변수로 받아 중심점(200px, 200px)에 원을 그린다. 

애니메이션 효과를 표현하는 <리스트 6>을 살펴보자.

<리스트 6> 애니메이션 효과를 표현하는 예제
(function() {
var rad, i = 0;
setInterval(function() { 
i++;
i = i % 360; 
rad = Math.sin(Math.PI / 180 * i) * 50 + 80;
context.clearRect(0, 0, 500, 400); 
drawCircle(rad); 
}, 33);
})();


<리스트 6>에는 스스로 실행되는 익명함수가 사용됐다. 자바스크립트에서 함수를 괄호로 감싼 뒤 그 뒤에 괄호를 추가하면 선언되며 실행되는 형태의 함수가 생성된다. 단 한번 실행되는 함수가 있을 때 유용하게 사용할 수 있는 방법이니 참고하자.

함수 내부에는 변수 ‘rad’와 ‘i’를 선언했다. rad는 반지름 값이며, i는 반지름 값을 계산하기 위해 동적으로 변하는 값이다. 

반지름 값인 i는 자바스크립트 Math 객체의 sin() 함수를 이용해 계산된다. 값이 진동하는 sin() 그래프의 특성을 이용한 것으로 원의 크기는 sin() 그래프의 y값과 같이 커졌다 작아졌다를 반복한다. sin() 함수의 인자로 라디안 값을 넘겨줘야 하며, Math.PI 값을 180으로 나눈 값에 0에서 360까지의 값을 곱해주는 방식으로 값을 바꿨다. 이런 방법으로 sin()은 -1에서 1까지의 값으로 진동한다. 여기에 50을 곱하고 80을 더해서 반지름이 30px에서 130px까지 변하는 원 애니메이션 효과를 완성했다.

참고로 원을 그리는 drawCircle() 함수를 호출하기 전에 clearRect() 함수를 먼저 호출했다. clearRect()는 캔버스를 지우는 함수로 이전에 그렸던 원이 지워진 후 새로운 원을 그리는 것이 반복돼 사람의 눈에는 애니메이션처럼 보이게 된다.

만약 clearRect() 함수가 없으면 이전에 그렸던 원들이 캔버스에 계속 남는 문제가 발생된다. clearRect() 함수는 점(0, 0)에서 점(500, 400) 범위를 지우므로 캔버스 전체를 지우는 것과 같다.
짧은 코드지만 <리스트 5>에 <리스트 6>을 추가하면 원의 크기가 동적으로 변하는 애니메이션을 볼 수 있다.

지금까지 캔버스 API를 활용해 가장 기본적인 그림 그리기와 애니메이션 효과를 구현해 봤다. 기초적이고 쉬운 코드이지만 기본을 잘 익혀 둔다면 더 역동적이고 화려한 고급 애니메이션 효과도 어렵지 않게 만들 수 있을 것이다.

참고자료
1. RGraph www.rgraph.net/
2. AwesomeChartJS cyberpython.github.com/AwesomeChartJS/
3. 구글 바디 bodybrowser.googlelabs.com
4. Mr. Doob mrdoob.com
5. Chrome WebGL Experiments www.chromeexperiments.com/webgl
6. SketchPad mugtug.com/sketchpad/