[HTML5] Canvas

Posted in 모바일/HTML5 // Posted at 2010.08.19 14:25
브라우저 위의 그림판
HTML5 의 많은 새로운 기능 중 가장 자주 언급되어 왔던 것이 아마 Canvas(캔버스) 일 것이다
Canvas 는 Web Workers 나 Web Storage와 같은 다른 HTML5 스펙보다 덜 기술적이며 보다 직관적이다.
개발자 뿐만 아니라 비개발자들 역시 Canvas의 개념을 쉽게 이해할 수 있고 흥미를 보여왔기 때문에 가장 먼저 그리고 가장 흔하게 소개된 HTML5 기술인 것이다

Canvas 는 말 그대로 그림을 그릴 수 있는 화폭, 즉 그림판과 같다
그것도 다른 곳에 아닌 브라우저 위에서 동작하는 그림판인 것이다

Canvas 위에 선, 도형, 텍스트, 이미지와 같은 그래픽을 표현할 수 있고 색깔, 그림자, 패턴과 같은 여러 효과를 적용할 수 있다. 그리고 기본적으로 Canvas 는 2차원 그래픽 표현을 위한 스펙이지만 추가로
WebGL를 기반으로 하는 3D 그래픽용 Canvas 의 스펙 개발이 진행중이다
참고: WebGL은 오픈 그래픽 라이브러리인 OpenGL(OpenGL ES 2.0)에 기반한 웹 표준 그래픽 라이브러리이다. WebGL을 이용하면 브라우저에 별도의 플러그 인 없이 완벽하게 하드웨어 가속되는 3D그래픽을 표현할 수 있게 된다. 주요 브라우저 벤더사인 애플, 구글, 모질라 및 오페라와 하드웨어 업체인 AMD 및 엔비디아 등이 WebGL Working Group 멤버로 활동하고 있다. 그리고 구글의 웹 용 3D API O3D 플러그 인 기술을 포기하고 WebGL 을 선택했다 


브라우저 지원 현황
브라우저 지원 현황 역시 다른 HTML5 기술보다 더 일반적이다
다시 말해 대부분의 브라우저에서 Canvas를 지원한다는 의미이다


그림1. 브라우저별 2D Canvas 지원 현황 (출처: http://caniuse.com/)

IE9 이전 버전에서 Canvas 지원하기
현재까지 출시된 IE8 까지는 HTML5 지원이 거의 되지 않는다
Canvas 역시 IE9 이전 버전에서는 동작하지 않는다
다만 구글에서 제공하는 크롬프레임을 설치하면 IE9 이전버전에서도 HTML5 를 동작시킬 수 있다

그러나 Canvas의 경우 더 쉽게 IE에 적용할 방법이 있다.
ExplorerCanvas 라이브러리를 이용하면 이전버전의 IE에서도 Canvas를 동작시킬 수 있게 된다
(ExplorerCanvas는 이전 버전의 IE에서 Canvas API가 동작할 수 있도록 해 주는 오픈소스 자바스크립트 라이브러리이다)
ExplorerCanvas 다운로드: http://code.google.com/p/explorercanvas/

다운받은 excanvas.js 스크립트 파일을 Canvas가 구현된 HTML 파일의 헤더 영역에 다음과 같이
정의하여 IE일 경우 이 스크립트가 적용되도록 처리하면 된다
<html>
<head>
 <!--[if IE]><script type="text/javascript" src="excanvas.js"></script><![endif]-->
  ...



Canvas 학습 지원 사이트
Canvas로 구현할 수 있는 것은 아주 많다
개념적으로 보편적인 기술인 만큼 그 응용 개발 역시 매우 다양하게 펼칠 수 있다
그러나 응용도 기본이 갖춰져야 제대로 되는 만큼 기본사용법의 정확한 숙지가 필요하다

이 글에서도 기본적인 사용법과 간단한 데모를 작성해 보겠지만,
그전에 Canvas 학습을 도와주는 훌륭한 사이트가 있어 소개한다

이 두 사이트에서 제공하는 Canvas 튜토리얼은 정확한 기본기를 갖추고 응용력을 기르는데 충분한 도움을 줄 것이다. Canvas를 적용할 서비스는 굉장히 많으며 특히 게임과 같은 동적이고 화려한 그래픽이 필요한 서비스에는 활용도가 무궁무진할 것이다. 잘 정리된 기본 학습 도구를 활용하는 것이 매우 중요하므로 위 사이트들을 꼭 참고하기 바란다


Canvas 다루기
지금부터 Canvas를 다루는 기본적인 방법에 대해 알아 보도록 하자

<canvas> 태그와 그리기 컨텍스트
그림을 그리기 위해서는 제일 먼저 브라우저에 화폭이 준비되어 있어야 한다
<canvas>라는 마크업 태그를 이용하여 브라우저에 캔버스 영역을 지정할 수 있다
<canvas id="cv" width="400" height="300"></canvas>

HTML 문서에 <canvas> 태그를 삽입하는 것 만으로 브라우저에 그림판이 준비된다
다만 캔버스는 기본적으로 테투리가 없기 때문에 위 코드만으로는 브라우저에서 캔버스를 눈으로 확인하기는 힘들다. 따라서 스타일을 적용해 캔버스의 테두리를 보이게 하는 것이 좋을 수 있다
<canvas id="cv" width="400" height="300"
    style="position: relative; border: 1px solid #000;"></canvas>

그리고 이 캔버스를 이용해 그리기를 수행하려면 '그리기 컨텍스트'를 얻어야 한다
그리기 컨텍스트는 앞서 정의한 <canvas> DOM 객체의 getContext 메서드로 얻을 수 있다
var canvas = document.getElementById("cv"); //canvas의 DOM 객체를 얻는다
var context = canvas.getContext("2d");         //DOM 객체로부터 2D 컨텍스트를 얻는다

이후 그리기 작업은 '그리기 컨텍스트'를 통해 이루어진다

캔버스의 좌표
캔버스에 각종 도형 및 선과 같은 2차원 그래픽 작업을 할 때 각 점의 위치를 지정해야 하는데
이는 캔버스의 2차원 좌표가 기준이 된다
사각형 모양을 하고 있는 캔버스는 제일 왼쪽 상단 꼭지점이 (0,0) 시작점이 된다
(0,0) 시작점을 기준으로 그리고자 하는 도형의 가로 위치 x 와 세로위치 y 좌표를 지정하면 된다

그림2. Canvas의 좌표 (출처: http://diveintohtml5.org/canvas.html)


그리기 작업

1) 사각형 그리기
그리기의 가장 기본이 되는 도형이다. Canvas API 에서 사각형을 위한 함수를 따로 제공하고 있다
아래와 같은 사각형 함수인데, 모두 같은 매개변수를 취하고 있다
x,y : 사각형의 시작점 좌표(왼쪽 위 모서리 지점), width,height: 사각형의 너비,높이

fillRect(x, y, width, height)       : 색으로 채운 사각형을 그린다
strokeRect(x, y, width, height) : 선만 있는 사각형을 그린다
clearRect(x, y, width, height)   : 사각형 영역을 지운다

아래 그림은 간단한 사각형 그리기 샘플이다
차례로 속이 찬 사각형과, 윤곽만 있는 사각형, 그리고 절반이 지워진 사각형이다
아래 결과 화면과 코드를 참고하기 바란다



<!DOCTYPE html>
<html>
<head></head>
<body>
  <canvas id="cv" width="400" height="300" style="position: relative; border: 1px solid #000;"></canvas
</body>
</html>
<script type="text/javascript">
  var canvas = document.getElementById("cv");
  var context = canvas.getContext("2d");          
 
  context.fillRect(10,10,100,100);    //색으로채운 사각형
  context.strokeRect(120,10,100,100); //윤곽만 있는 사각형
 
  context.fillRect(230,10,100,100);   //색으로 채운 사각형
  context.clearRect(230,10,50,50);    //앞 사각형의 절반을 지움
</script> 


2) 각종 도형 및 선 그리기
사각형과는 달리, 삼각형, 오각형, 육각형 및 원과 같은 도형은 선들을 연결하여 직접 그려줘야 한다
완성된 도형은 선들의 집합체이며 각각의 선들을 패스(Path) 라 한다
(엄밀히 말하자면 각 선은 서브패스이며 서브패스 전체를 가리켜 패스라 한다)

결국 도형을 그린다는 것은 각 선을 연결한다는 의미이며 이는 패스(Path)를 그리는 과정인 것이다

패스(Path)그리기 순서
첫째, 패스 그리기를 시작을 알린다
: beginPath() 함수로 canvas에 패스그리기를 알린다. 이전의 패스는 모두 초기화된다

둘째, 패스 경로 즉 시작점과 종료점을 지정한다
: moveTo(x,y) 함수로 패스의 시작점을 지정하고 lineTo(x,y) 함수로 패스가 이어질 점을 지정한다
 
셋째, 패스 경로를 따라 실제로 그린다
: 이전 과정까지는 패스 즉 경로에 대한 정보를 셋팅하는 것이다
  실제 지정된 패스를 따라 선을 그려야 하는데, 이는 stroke(), fill()과 같은 잉크(ink)함수가 이용된다

삼각형 그리기
간단한 삼각형 그리기 샘플을 보자.
아래와 같은 삼각형을 그리기 위해 패스그리기를 수행한 예이다


<script type="text/javascript">
  var canvas = document.getElementById("cv");
  var context = canvas.getContext("2d");          
 
  context.beginPath(); //패스 그리기 시작  
  context.moveTo(50,10); //패스 시작점 지정  
  context.lineTo(20 ,100); //패스 이동점 지정
  context.lineTo(80 ,100); 
  context.lineTo(50 ,10);  
  context.stroke();    //윤관선 그리기
  //context.fill();   //색 채우기
</script>

beginPath() 로 패스그리기의 시작을 알리고 moveTo()로 패스 시작점을 설정한다
그리고 lineTo() 를 이용하여 패스의 경로를, 그리는 방향 대로 이동한다
마지막으로 stroke()로 패스의 윤곽선을 그린다
주석처리된 fill() 함수로 패스를 그릴 경우 패스 속이 색(기존 검정색)으로 채워지게 된다 

이런식으로 삼각형은 물론, 사각형, 오각형,.. N각형의 도형을 맘껏 그릴 수 있다

원 그리기
앞서 살펴본 삼각형 그리기는 직선을 연결한 도형일 경우에는 적합하지만 곡선이나 원을
그릴 수는 없다. 즉 lineTo() 함수는 직선을 그리는 함수이다

원이나 곡선 역시 패스 그리기의 일종으로 앞서 살펴본 과정을 그대로 따른다
다만 직선을 그리는 함수인 lineTo() 대신 원을 그리는 함수인 arc() 나 배지곡선을 그리는 함수인 quadraticCurveTo() 함수를 이용하면 된다

간단한 원그리기 샘플을 살펴 보자


<script type="text/javascript">
  var canvas = document.getElementById("cv");
  var context = canvas.getContext("2d");          
 
  context.beginPath(); //패스 그리기 시작   
  context.arc(70,70,50,0, 2*Math.PI, true); //원그리기 
  context.stroke();    //윤관선 그리기
</script>


패스 닫기
패스를 이용해 도형을 마음껏 그릴 수 있으려면 관련 학습을 조금 더 해야 한다
패스와 관련된 추가 학습은 다른 사이트를 참고하도록 하며 이 글에서는 명시적으로 패스를 닫는 closePath() 함수에 대해 알아보면서 패스 정리를 마치고자 한다

closePath()는 패스를 명시적으로 닫는 역할을 한다
반드시 사용해야 하는 것은 아니지만 명시적으로 closePath()를 호출해 주면
마지막 패스의 좌표와 시작좌표를 자동으로 연결해 주기 때문에 편리하다

다음은 앞서 살펴본 삼각형 그리기 예에서 마지막 패스를 생략하고 closePath()를 대신 사용하였다
즉 마지막 lineTo() 함수가 없어도 자동으로 시작점과 연결해 주기 때문에 결과는 동일하다
<script type="text/javascript">
  var canvas = document.getElementById("cv");
  var context = canvas.getContext("2d");          
 
  context.beginPath(); //패스 그리기 시작 
  context.moveTo(50,10); //패스 시작점 지정 
  context.lineTo(20 ,100); //패스 이동점 지정
  context.lineTo(80 ,100); 
  context.closePath();     //마지막 패스 이동점 대신 패스를 명시적으로 닫는다
  context.stroke();    //윤관선 그리기
</script>


스타일 및 효과
캔버스에 그리기 작업을 할 경우 별다른 스타일을 지정하지 않으면 기본 스타일이 적용된다
앞서 살펴본 샘플에서 도형의 안,팎의 색이 검정색이었던 것은 기본값이기 때문이다
스타일을 적용하면 도형의 색 지정 뿐만 아니라 그라데이션 효과, 그림자 효과등을 줄 수 있다

색 지정
우선 간단히 색을 지정하는 예를 보자. 앞서 살펴본 사각형 그리기 예에서 색을 지정한다
채우기 스타일은 fillStyle 로 윤곽선 스타일은 strokeStyle 로 색을 지정하면 된다
색을 지정할 때 blue, red 와 같은 문자는 물론 #ffffff 혹은 rgb(255,0,0) 등 모든 CSS 컬러를 사용할 수 있다. 색을 지정하지 않았을 땐 기본값으로 #000000(검정색) 이 된다


<script type="text/javascript">
  var canvas = document.getElementById("cv");
  var context = canvas.getContext("2d");                 
 
  context.fillStyle = "blue";
  context.fillRect(10,10,100,100);    //파란색으로 채운 사각형
 
  context.strokeStyle = "red";
  context.strokeRect(120,10,100,100); //빨가색 윤곽선 사각형
</script>


그라데이션 효과
fillStyle 이나 strokeStyle를 통해 색깔 지정 뿐만 아니라 그라데이션 효과도 줄 수 있다
그라데이션은 선형그라데이션과 원형그라데이션 두 가지 종류가 있다

선형 그라데이션
: (x,y) 지점에서 (x1,y1) 까지 직선으로 색조가 변화한다
  createLinearGradient(x,y,x1,y1) 함수 이용

원형 그리데이션
: 중심이 (x,y), 반지름 r인 원에서 중심이 (x1,y1), 반지름이 r1 인 원 사이의 색조가 변화한다
  createRadialGradient(x,y,r,x1,y1,r1) 함수 이용

createXXXGradient 함수로 그라데이션의 형태를 지정한 후,
addColorStop(offset, color) 함수를 이용하여 각 지점에 이용될 색을 지정한다
offset는 0.0(시작점) 에서 1.0 (끝점) 사이의 위치 값을 나타내며 해당 위치의 색을 지정한다

사각형에 선형 그라데이션 효과를 준 예를 살펴 보자
두 사각형 모두 선형 그라데이션 효과를 준 것이며 직선 방향을 조정하여 가로,세로 효과를 줬다

(화살표는 색조 변화 방향을 나타냄)

<script type="text/javascript">
  var canvas = document.getElementById("cv");
  var context = canvas.getContext("2d");                 
 
  var gradient = context.createLinearGradient(0,0,100,0);
  gradient.addColorStop(0,"white");
  gradient.addColorStop(1,"blue"); 
  context.fillStyle = gradient;   
  context.fillRect(10,10,100,100); 
 
  gradient = context.createLinearGradient(0,0,0,100);
  gradient.addColorStop(0,"white");
  gradient.addColorStop(1,"red"); 
  context.strokeStyle = gradient;
  context.strokeRect(120,10,100,100);      
</script>


그림자 효과
선이나 도형에 그림자 효과를 줄 수 있다.
shadowOffsetX, shadowOffsetY 함수를 이용하여 원본 도형의 위치를 기준으로 그림자의 위치를 지정할 수 있으며 shadowColor 로 그림자 색상 shadowBlur로 그림자의 흐리기를 조정할 수 있다


<script type="text/javascript">
  var canvas = document.getElementById("cv");
  var context = canvas.getContext("2d");                 
    
  context.shadowColor = "green";
  context.shadowOffsetX = 5;
  context.shadowOffsetY = 5;
  context.shadowBlur = 2; 
 
  context.fillStyle = "blue";
  context.fillRect(10,10,100,100);  
</script>



이미지 및 텍스트 그리기
캔버스에는 선이나 각종 도형 이외에도 이미지 파일을 삽입하거나 텍스트를 직접 그릴 수도 있다
캔버스에 텍스트 그리기를 이용하면 글자에 각종 효과를 줄 수 있는 장점이 있다
그리고 이미지 역시 각종 이미지 처리를 할 수 있다는 장점이 있겠다

텍스트 그리기
우선 캔버스에 텍스트를 그리는 예를 살펴 보자
다른 도형 그리기와 마찬가지로 채워진 텍스트 혹은 윤곽선만 있는 텍스트를 그릴 수 있다


<script type="text/javascript">
  var canvas = document.getElementById("cv");
  var context = canvas.getContext("2d");                 
    
  var msg = "Hello, Canvas!"
 
  context.font = "20px '맑은 고딕'";
  context.fillText(msg, 10  ,50);
 
  context.font = "40px 'Tahoma'";
  context.strokeText(msg, 10  ,100);
</script>


이미지 그리기
drawImage 함수를 이용하여 캔버스에 이미지 파일을 삽입할 수 있다
주의할 점은 drawImage의 호출 시점이다. 이미지가 모두 로딩된 이후에 이 함수를 호출해야 한다
따라서 이미지 로딩이 완료될 때 발생하는 onload 이벤트에서 이 함수를 사용하는 것이 일반적이다

눈의 즐거움을 위해 손담비 사진을 이용해 샘플을 만들어 보자 ^.^
Image 객체의 onload 이벤트에서 drawImage 함수가 호출되는 것을 눈여겨 보기 바라며
이미지를 이용하여 사각형을 그릴 때 채우기 패턴으로 사용할 수도 있다
예에서는 손담비 이미지를 기반으로 패턴을 생성하여 사각형의 채우기 스타일로 사용하고 있다
그리고 회전효과도 가미했다(이미지 그리기와는 무관하지만...)


<script type="text/javascript">
  var canvas = document.getElementById("cv");
  var context = canvas.getContext("2d");                 
    
  var image = new Image();
 
  image.onload = function(){
    context.rotate(20*Math.PI/180);       //회전효과
   
    context.drawImage(image,30,10);   //이미지 그리기
   
    var pattern = context.createPattern(image,"repeat"); //반복패턴정의
    context.fillStyle = pattern;                                       //fill 패턴 지정
    context.fillRect(200,10,200,250);                                   //이미지로 사각형을 채운다     
  }
 
  image.src = "sondambi.bmp";
</script>


Canvas 초기화
그림을 그리다 모두 지우고 싶을 때가 있다
캔버스에 각종 그래픽 작업을 하다가 모든 것을 지우고 다시 시작하고 싶은,
일명 리셋(reset)을 하고 싶다면 매우 단순하게 초기화를 할 수 있다

캔버스의 너비나 높이 속성을 다시 설정하는 것만으로 캔버스는 완전 초기화 된다
캔버스에 그려진 내용 뿐만 아니라 그리기컨텍스트의 각종 속성도 기본값으로 돌아 온다

function resetCanvas(){
    canvas.width = canvas.width;       
  }


마무리 데모
이제 이 글을 마치기 전에 마무리 데모를 살펴 보자
사실 이 글에서 소개된 캔버스 다루기 내용은 극히 일부에 지나지 않는다.
2D 그래픽 처리와 관련한 다양한 기법과 API들이 존재한다.

반드시 관련 자료를 참고하여 추가 학습을 하길 권하며 3D 캔버스인 WebGL도 필요하다면 관련 자료를
찾아 보기 바란다

마지막 데모는 마우스로 그림을 그릴 수 있는 그림판이다
캔버스에 마우스를 데고 자유자재로 움직이면, 선이 마우스를 따라 그려지는 샘플이다

아래 데모 실행화면을 보자. 필자가 캔버스에 대고 그린 그림이다 -.-;


전체 소스
<!DOCTYPE html>
<html>
  <head>
    <title>Canvas Paint Example</title>      
    <!--[if IE]><script type="text/javascript" src="excanvas.js"></script><![endif]-->   
  </head>
  <body>
   <div id="container">
     <canvas id="drawCanvas" width="400" height="300"
            style=" position: relative; border: 1px solid #000;"></canvas>
</div>
</body>
</html>
<script type="text/javascript">   
if(window.addEventListener){
    window.addEventListener('load', InitEvent, false);
}
var canvas, context, tool;       
function InitEvent () {                                      
    canvas = document.getElementById('drawCanvas');
    if (!canvas) {
      alert("캔버스 객체를 찾을 수 없음");
      return;
    }
    if (!canvas.getContext) {
      alert("Drawing Contextf를 찾을 수 없음");
      return;
    }       
    context = canvas.getContext('2d');
    if (!context) {
      alert("getContext() 함수를 호출 할 수 없음");
      return;
    }
    // Pencil tool 객체를 생성 한다.
    tool = new tool_pencil();
    canvas.addEventListener('mousedown', ev_canvas, false);
    canvas.addEventListener('mousemove', ev_canvas, false);
    canvas.addEventListener('mouseup',   ev_canvas, false);
}
function tool_pencil ()                                  
{
    var tool = this;
    this.started = false;
    // 마우스를 누르는 순간 그리기 작업을 시작 한다.
    this.mousedown = function (ev)
    {
        context.beginPath();
        context.moveTo(ev._x, ev._y);
        tool.started = true;
    };
   // 마우스가 이동하는 동안 계속 호출하여 Canvas에 Line을 그려 나간다
    this.mousemove = function (ev)
    {
        if (tool.started)
        {
            context.lineTo(ev._x, ev._y);
            context.stroke();
        }
    };
   // 마우스 떼면 그리기 작업을 중단한다
    this.mouseup = function (ev)
    {
      if (tool.started){
            tool.mousemove(ev);
            tool.started = false;
      }
    };
}       
// Canvas요소 내의 좌표를 결정 한다.
function ev_canvas (ev)
{
    if (ev.layerX || ev.layerX == 0)
    { // Firefox 브라우저
      ev._x = ev.layerX;
      ev._y = ev.layerY;
    }
    else if (ev.offsetX || ev.offsetX == 0)
    { // Opera 브라우저
      ev._x = ev.offsetX;
      ev._y = ev.offsetY;
    }
    // tool의 이벤트 핸들러를 호출한다.
    var func = tool[ev.type];       
    if (func) {
        func(ev);
    }
}
</script>

'모바일 > HTML5' 카테고리의 다른 글

HTML5 관련 유용한 레퍼(참조) 사이트  (0) 2010.08.26
[HTML5] Video & Audio  (6) 2010.08.24
[HTML5] Canvas  (3) 2010.08.19
[HTML5] Web SQL Database  (0) 2010.08.17
[HTML5] 오프라인 웹 어플리케이션  (0) 2010.08.12
[HTML5] Web Storage  (1) 2010.08.11
Tags ,
  1. sangyun

    정말 감사합니다. HTML5에 대한 글을 잘 보고 있습니다.

  2. 기린아식빵먹자

    최근에 캔버스만 이용해 홈페이지를 만들었는데. 만들면서 느낀점은 아직 캔버스 호환이 많이 부족한거같아요. 확장자도 여러 웹브라우저에 호환이 안되고 일일이 다 만들어야한다는점이 아직 너무 불편하네요. 저는 그래서 크롬과 파폭만 작동되게 해놨다능... ㅋ

submit