Drawing Graphics with Canvas

  • 리비전 슬러그: Drawing_Graphics_with_Canvas
  • 리비전 제목: Drawing Graphics with Canvas
  • 리비전 아이디: 161852
  • 제작일시:
  • 만든이: 토끼군
  • 현재 리비전인가요? 아니오
  • 댓글 전체 번역

리비전 내용

소개

Firefox 1.5에서 Firefox는 프로그래밍이 가능한 새로운 HTML 요소를 포함합니다. <canvas>는 Apple이 사파리에 <canvas>를 구현할 때 기반이 되었던 WHATWG canvas 명세를 기반으로 하고 있습니다. 그래프를 그리거나, UI 요소, 그 외에도 클라이언트에서의 다른 여러 그래픽 작업에 사용될 수 있습니다.

<canvas>는 크기가 고정되어 있으며 하나 이상의 렌더링 문맥을 가지는 그리기 영역을 만듭니다. 우리는 (현재 유일하게 정의되어 있기도 한) 2차원 렌더링 문맥에 초점을 맞출 것입니다. 나중에는 다른 렌더링 방법을 지원하는 문맥이 만들어질 수도 있습니다; 예를 들어서, <canvas> 명세에 OpenGL ES에 기반한 3차원 렌더링 문맥이 추가될 가능성이 높습니다.

2차원 렌더링 문맥

간단한 예제

우선 겹치는 두 사각형을 그리는 간단한 예제부터 시작합시다. 그 중 하나에는 알파 투명도도 적용되어 있습니다:

예제 1.

<html>
 <head>
  <script type="application/x-javascript">
function draw() {
 var canvas = document.getElementById("canvas");
 var ctx = canvas.getContext("2d");

 ctx.fillStyle = "rgb(200,0,0)";
 ctx.fillRect (10, 10, 50, 50);

 ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
 ctx.fillRect (30, 30, 50, 50);
}
  </script>
 </head>
 <body onload="draw()">
   <canvas id="canvas" width="300" height="300"></canvas>
 </body>
</html>

draw 함수는 canvaa 요소를 받아서 2d 문맥을 얻습니다. ctx 객체는 실제로 캔버스에 렌더링하는데 사용되었습니다. 예제는 단순히 두 사각형을 색칠하는데, CSS 색 명세를 써서 fillStyle(채움 스타일)에 서로 다른 두 색을 지정하고 fillRect 메소드를 호출합니다. 두번째 fillStyle에서는 rgba()를 사용해서 색상과 함께 알파 값을 지정했습니다.

fillRect, strokeRect, clearRect는 각각 채워진 사각형을 그리거나, 사각형의 외곽선을 그리거나, 사각형 영역을 지웁니다. 복잡한 모양을 그리려면 경로(path)를 사용합니다.

경로 사용하기

beginPath 함수는 새로운 경로를 시작하고, moveTo, lineTo, arcTo, arc 등의 메소드는 경로의 부분을 추가하기 위해 사용됩니다. 경로는 closePath로 끝맺을 수 있습니다. 한 번 경로가 만들어지면, fill이나 stroke 메소드를 사용해 캔버스에 경로를 렌더링할 수 있습니다.

예제 2.

<html>
 <head>
  <script type="application/x-javascript">
function draw() {
  var canvas = document.getElementById("canvas");
  var ctx = canvas.getContext("2d");

  ctx.fillStyle = "red";

  ctx.beginPath();
  ctx.moveTo(30, 30);
  ctx.lineTo(150, 150);
  ctx.quadraticCurveTo(60, 70, 70, 150);
  ctx.lineTo(30, 30);
  ctx.fill();
}
   </script>
 </head>
 <body onload="draw()">
   <canvas id="canvas" width="300" height="300"></canvas>
 </body>
</html>

fill()stroke()를 호출하면 현재 경로는 다시 사용할 수 없습니다. 다시 한 번 경로 영역을 채우거나 외곽선을 그리려면 새로 경로를 생성해야 합니다.

그래픽 상태

fillStyle, strokeStyle, lineWidth, lineJoin 같이 문맥 객체에 있는 속성들은 현재 그래픽 상태의 일부분입니다. 문맥에서는 현재 상태를 상태 스택에 넣거나 빼는 save()restore() 메소드를 제공합니다.

좀 더 복잡한 예제

다음은 조금 더 복잡한 예제로, 경로와 상태를 사용하며 현재 변환 행렬이 새로 등장합니다. 문맥 메소드 translate(), scale(), rotate()는 모두 현재 행렬을 변환시킵니다. 렌더링되는 모든 점들은 먼저 이 행렬을 써서 변환됩니다.

예제 3.

 <html>
  <head>
   <script type="application/x-javascript">
 function drawBowtie(ctx, fillStyle) {
 
   ctx.fillStyle = "rgba(200,200,200,0.3)";
   ctx.fillRect(-30, -30, 60, 60);
 
   ctx.fillStyle = fillStyle;
   ctx.globalAlpha = 1.0;
   ctx.beginPath();
   ctx.moveTo(25, 25);
   ctx.lineTo(-25, -25);
   ctx.lineTo(25, -25);
   ctx.lineTo(-25, 25);
   ctx.closePath();
   ctx.fill();
 }
 
 function dot(ctx) {
   ctx.save();
   ctx.fillStyle = "black";
   ctx.fillRect(-2, -2, 4, 4);
   ctx.restore();
 }
 
 function draw() {
   var canvas = document.getElementById("canvas");
   var ctx = canvas.getContext("2d");

   // 앞으로 나올 모든 변환들은 이 변환에 상대적임
   ctx.translate(45, 45);

   ctx.save();
   //ctx.translate(0, 0); // 불필요함
   drawBowtie(ctx, "red");
   dot(ctx);
   ctx.restore();
 
   ctx.save();
   ctx.translate(85, 0);
   ctx.rotate(45 * Math.PI / 180);
   drawBowtie(ctx, "green");
   dot(ctx);
   ctx.restore();
 
   ctx.save();
   ctx.translate(0, 85);
   ctx.rotate(135 * Math.PI / 180);
   drawBowtie(ctx, "blue");
   dot(ctx);
   ctx.restore();
 
   ctx.save();
   ctx.translate(85, 85);
   ctx.rotate(90 * Math.PI / 180);
   drawBowtie(ctx, "yellow");
   dot(ctx);
   ctx.restore();
 }
    </script>
  </head>
  <body onload="draw()">
    <canvas id="canvas" width="300" height="300"></canvas>
  </body>
 </html>

여기서는 네 번 호출되는 두 메소드 drawBowtiedot을 정의합니다. 각각의 호출 전에 translate()rotate()가 현재 변환 행렬을 준비하는 데 사용되는데, 이는 점과 나비 넥타이 모양의 위치를 지정합니다. dot(0, 0)이 중심인 작고 까만 사각형을 그립니다. 이 점은 변환 행렬에 따라 이동됩니다. drawBowtie는 간단한 나비 넥타이 경로를 인자로 받은 채움 스타일에 따라 그립니다.

행렬 연산이 계속 누적되기 때문에, 매번 호출할 때마다 save()restore()를 써서 원래 캔버스 상태를 되돌립니다. 주의해야 할 점으로는 회전이 항상 현재 원점을 기준으로 일어 난다는 것입니다; 따라서 translate() rotate() translate() 순서대로 호출한 결과는 translate() translate() rotate()와 다른 결과를 낼 것입니다.

Apple <canvas>와의 호환성

대부분의 경우 <canvas>는 Apple의 구현과 다른 구현들과 호환됩니다. 하지만 몇 가지 알 필요가 있는 사항은 여기서 설명합니다.

</canvas> 태그의 필요

Apple 사파리 구현에서는 <canvas> 엘리먼트가 <img>와 매우 비슷한 방법으로 구현됩니다; 닫는 태그가 필요하지 않습니다. 하지만 <canvas>가 웹 상에서 널리 쓰이기 위해서는 뭔가 대체 내용을 넣는 방법을 제공해야 합니다. 따라서 모질라의 구현에서는 닫는 태그가 꼭 필요합니다.

만약 대체 내용이 필요하지 않다면, 간단하게 <canvas id="foo" ...></code>라고 해도 사파리와 모질라에서 완전히 호환될 것입니다 -- 사파리는 그냥 닫는 태그를 무시할 것입니다.

대체 내용이 필요하다면, 약간의 CSS 트릭을 써서 (캔버스 뒤에 표시될) 대체 내용을 사파리가 보여주지 않도록 해야 하며, 이 내용이 IE에서는 보이도록 다시 CSS 트릭을 써야 합니다. Todo: get hixie to put the CSS bits in

추가적인 기능

웹 내용을 캔버스에 렌더링하기

이 기능은 크롬 권한을 가진 코드만 사용할 수 있습니다. 보통 HTML 페이지에서는 허용되지 않습니다.

모질라의 canvasdrawWindow 메소드로 확장할 수 있습니다. 이 메소드는 DOM window의 현재 내용을 캔버스에 그립니다. 예를 들어서,

ctx.drawWindow(window, 0, 0, 100, 200, "rgb(0,0,0)");

이 코드는 현재 창의 내용을 캔버스의 (0,0)-(100,200) 사각형에 검은 바탕으로 그리는데, 좌표는 뷰포트(viewport)의 왼쪽 위 모서리에 상대적입니다. 색깔을 "rgba(0,0,0,0)"으로 지정하면 내용은 투명 배경으로 그려질 것입니다 (느려질 수 있음).

이 방법을 사용하면, 임의의 내용(CSS 스타일이 적용된 HTML 텍스트나 SVG)이 들어 있는 숨겨진 IFRAME을 채우고 캔버스에 그릴 수 있습니다. 현재 변환에 따라 확대 및 축소하거나, 회전도 가능합니다.

Ted Mielczarek의 tab preview 확장기능은 이 기법을 크롬에서 웹 페이지의 축소 이미지를 만드는 데 사용했고, 소스 코드는 참고를 위해 공개되어 있습니다.

같이 보기

{{ wiki.languages( { "fr": "fr/Dessiner_avec_canvas", "ja": "ja/Drawing_Graphics_with_Canvas", "ko": "ko/Drawing_Graphics_with_Canvas", "pl": "pl/Rysowanie_grafik_za_pomoc\u0105_Canvas" } ) }}

리비전 소스

<h3 name=".EC.86.8C.EA.B0.9C"> 소개 </h3>
<p><a href="ko/Firefox_1.5">Firefox 1.5</a>에서 Firefox는 프로그래밍이 가능한 새로운 HTML 요소를 포함합니다. <code>&lt;canvas&gt;</code>는 Apple이 사파리에 <code>&lt;canvas&gt;</code>를 구현할 때 기반이 되었던 <a class="external" href="http://www.whatwg.org/specs/web-apps/current-work/#scs-dynamic">WHATWG canvas 명세</a>를 기반으로 하고 있습니다. 그래프를 그리거나, UI 요소, 그 외에도 클라이언트에서의 다른 여러 그래픽 작업에 사용될 수 있습니다.
</p><p><code>&lt;canvas&gt;</code>는 크기가 고정되어 있으며 하나 이상의 <i>렌더링 문맥</i>을 가지는 그리기 영역을 만듭니다. 우리는 (현재 유일하게 정의되어 있기도 한) 2차원 렌더링 문맥에 초점을 맞출 것입니다. 나중에는 다른 렌더링 방법을 지원하는 문맥이 만들어질 수도 있습니다; 예를 들어서, <code>&lt;canvas&gt;</code> 명세에 OpenGL ES에 기반한 3차원 렌더링 문맥이 추가될 가능성이 높습니다.
</p>
<h3 name="2.EC.B0.A8.EC.9B.90_.EB.A0.8C.EB.8D.94.EB.A7.81_.EB.AC.B8.EB.A7.A5"> 2차원 렌더링 문맥 </h3>
<h4 name=".EA.B0.84.EB.8B.A8.ED.95.9C_.EC.98.88.EC.A0.9C"> 간단한 예제 </h4>
<p>우선 겹치는 두 사각형을 그리는 간단한 예제부터 시작합시다. 그 중 하나에는 알파 투명도도 적용되어 있습니다:
</p><p><img align="right" alt="예제 1." src="File:ko/Media_Gallery/Canvas_ex1.png">
</p>
<pre class="eval">&lt;html&gt;
 &lt;head&gt;
  &lt;script type="application/x-javascript"&gt;
function draw() {
 var canvas = document.getElementById("canvas");
 var ctx = canvas.getContext("2d");

 ctx.fillStyle = "rgb(200,0,0)";
 ctx.fillRect (10, 10, 50, 50);

 ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
 ctx.fillRect (30, 30, 50, 50);
}
  &lt;/script&gt;
 &lt;/head&gt;
 &lt;body onload="draw()"&gt;
   &lt;canvas id="canvas" width="300" height="300"&gt;&lt;/canvas&gt;
 &lt;/body&gt;
&lt;/html&gt;
</pre>
<p><code>draw</code> 함수는 <code>canvaa</code> 요소를 받아서 <code>2d</code> 문맥을 얻습니다. <code>ctx</code> 객체는 실제로 캔버스에 렌더링하는데 사용되었습니다. 예제는 단순히 두 사각형을 색칠하는데, CSS 색 명세를 써서 fillStyle(채움 스타일)에 서로 다른 두 색을 지정하고 <code>fillRect</code> 메소드를 호출합니다. 두번째 fillStyle에서는 <code>rgba()</code>를 사용해서 색상과 함께 알파 값을 지정했습니다.
</p><p><code>fillRect</code>, <code>strokeRect</code>, <code>clearRect</code>는 각각 채워진 사각형을 그리거나, 사각형의 외곽선을 그리거나, 사각형 영역을 지웁니다. 복잡한 모양을 그리려면 경로(path)를 사용합니다.
</p>
<h4 name=".EA.B2.BD.EB.A1.9C_.EC.82.AC.EC.9A.A9.ED.95.98.EA.B8.B0"> 경로 사용하기 </h4>
<p><code>beginPath</code> 함수는 새로운 경로를 시작하고, <code>moveTo</code>, <code>lineTo</code>, <code>arcTo</code>, <code>arc</code> 등의 메소드는 경로의 부분을 추가하기 위해 사용됩니다. 경로는 <code>closePath</code>로 끝맺을 수 있습니다. 한 번 경로가 만들어지면, <code>fill</code>이나 <code>stroke</code> 메소드를 사용해 캔버스에 경로를 렌더링할 수 있습니다.
</p><p><img align="right" alt="예제 2." src="File:ko/Media_Gallery/Canvas_ex2.png">
</p>
<pre class="eval">&lt;html&gt;
 &lt;head&gt;
  &lt;script type="application/x-javascript"&gt;
function draw() {
  var canvas = document.getElementById("canvas");
  var ctx = canvas.getContext("2d");

  ctx.fillStyle = "red";

  ctx.beginPath();
  ctx.moveTo(30, 30);
  ctx.lineTo(150, 150);
  ctx.quadraticCurveTo(60, 70, 70, 150);
  ctx.lineTo(30, 30);
  ctx.fill();
}
   &lt;/script&gt;
 &lt;/head&gt;
 &lt;body onload="draw()"&gt;
   &lt;canvas id="canvas" width="300" height="300"&gt;&lt;/canvas&gt;
 &lt;/body&gt;
&lt;/html&gt;
</pre>
<p><code>fill()</code>나 <code>stroke()</code>를 호출하면 현재 경로는 다시 사용할 수 없습니다. 다시 한 번 경로 영역을 채우거나 외곽선을 그리려면 새로 경로를 생성해야 합니다.
</p>
<h4 name=".EA.B7.B8.EB.9E.98.ED.94.BD_.EC.83.81.ED.83.9C"> 그래픽 상태 </h4>
<p><code>fillStyle</code>, <code>strokeStyle</code>, <code>lineWidth</code>, <code>lineJoin</code> 같이 문맥 객체에 있는 속성들은 현재 <i>그래픽 상태</i>의 일부분입니다. 문맥에서는 현재 상태를 상태 스택에 넣거나 빼는 <code>save()</code>와 <code>restore()</code> 메소드를 제공합니다.
</p>
<h4 name=".EC.A2.80_.EB.8D.94_.EB.B3.B5.EC.9E.A1.ED.95.9C_.EC.98.88.EC.A0.9C"> 좀 더 복잡한 예제 </h4>
<p>다음은 조금 더 복잡한 예제로, 경로와 상태를 사용하며 현재 변환 행렬이 새로 등장합니다. 문맥 메소드 <code>translate()</code>, <code>scale()</code>, <code>rotate()</code>는 모두 현재 행렬을 변환시킵니다. 렌더링되는 모든 점들은 먼저 이 행렬을 써서 변환됩니다.
</p><p><img align="right" alt="예제 3." src="File:ko/Media_Gallery/Canvas_ex3.png">
</p>
<pre> &lt;html&gt;
  &lt;head&gt;
   &lt;script type="application/x-javascript"&gt;
 function drawBowtie(ctx, fillStyle) {
 
   ctx.fillStyle = "rgba(200,200,200,0.3)";
   ctx.fillRect(-30, -30, 60, 60);
 
   ctx.fillStyle = fillStyle;
   ctx.globalAlpha = 1.0;
   ctx.beginPath();
   ctx.moveTo(25, 25);
   ctx.lineTo(-25, -25);
   ctx.lineTo(25, -25);
   ctx.lineTo(-25, 25);
   ctx.closePath();
   ctx.fill();
 }
 
 function dot(ctx) {
   ctx.save();
   ctx.fillStyle = "black";
   ctx.fillRect(-2, -2, 4, 4);
   ctx.restore();
 }
 
 function draw() {
   var canvas = document.getElementById("canvas");
   var ctx = canvas.getContext("2d");

   // 앞으로 나올 모든 변환들은 이 변환에 상대적임
   ctx.translate(45, 45);

   ctx.save();
   //ctx.translate(0, 0); // 불필요함
   drawBowtie(ctx, "red");
   dot(ctx);
   ctx.restore();
 
   ctx.save();
   ctx.translate(85, 0);
   ctx.rotate(45 * Math.PI / 180);
   drawBowtie(ctx, "green");
   dot(ctx);
   ctx.restore();
 
   ctx.save();
   ctx.translate(0, 85);
   ctx.rotate(135 * Math.PI / 180);
   drawBowtie(ctx, "blue");
   dot(ctx);
   ctx.restore();
 
   ctx.save();
   ctx.translate(85, 85);
   ctx.rotate(90 * Math.PI / 180);
   drawBowtie(ctx, "yellow");
   dot(ctx);
   ctx.restore();
 }
    &lt;/script&gt;
  &lt;/head&gt;
  &lt;body onload="draw()"&gt;
    &lt;canvas id="canvas" width="300" height="300"&gt;&lt;/canvas&gt;
  &lt;/body&gt;
 &lt;/html&gt;
</pre>
<p>여기서는 네 번 호출되는 두 메소드 <code>drawBowtie</code>와 <code>dot</code>을 정의합니다. 각각의 호출 전에 <code>translate()</code>와 <code>rotate()</code>가 현재 변환 행렬을 준비하는 데 사용되는데, 이는 점과 나비 넥타이 모양의 위치를 지정합니다. <code>dot</code>은 <code>(0, 0)</code>이 중심인 작고 까만 사각형을 그립니다. 이 점은 변환 행렬에 따라 이동됩니다. <code>drawBowtie</code>는 간단한 나비 넥타이 경로를 인자로 받은 채움 스타일에 따라 그립니다.
</p><p>행렬 연산이 계속 누적되기 때문에, 매번 호출할 때마다 <code>save()</code>와 <code>restore()</code>를 써서 원래 캔버스 상태를 되돌립니다. 주의해야 할 점으로는 회전이 항상 현재 원점을 기준으로 일어 난다는 것입니다; 따라서 <code>translate() rotate() translate()</code> 순서대로 호출한 결과는 <code>translate() translate() rotate()</code>와 다른 결과를 낼 것입니다.
</p>
<h3 name="Apple_.3Ccanvas.3E.EC.99.80.EC.9D.98_.ED.98.B8.ED.99.98.EC.84.B1"> Apple &lt;canvas&gt;와의 호환성 </h3>
<p>대부분의 경우 <code>&lt;canvas&gt;</code>는 Apple의 구현과 다른 구현들과 호환됩니다. 하지만 몇 가지 알 필요가 있는 사항은 여기서 설명합니다.
</p>
<h4 name=".3C.2Fcanvas.3E_.ED.83.9C.EA.B7.B8.EC.9D.98_.ED.95.84.EC.9A.94"> <code>&lt;/canvas&gt;</code> 태그의 필요 </h4>
<p>Apple 사파리 구현에서는 <code>&lt;canvas&gt;</code> 엘리먼트가 <code>&lt;img&gt;</code>와 매우 비슷한 방법으로 구현됩니다; 닫는 태그가 필요하지 않습니다. 하지만 <code>&lt;canvas&gt;</code>가 웹 상에서 널리 쓰이기 위해서는 뭔가 대체 내용을 넣는 방법을 제공해야 합니다. 따라서 모질라의 구현에서는 닫는 태그가 꼭 필요합니다.
</p><p>만약 대체 내용이 필요하지 않다면, 간단하게 <code>&lt;canvas id="foo" ...&gt;</code>&lt;/code&gt;라고 해도 사파리와 모질라에서 완전히 호환될 것입니다 -- 사파리는 그냥 닫는 태그를 무시할 것입니다.
</p><p>대체 내용이 필요하다면, 약간의 CSS 트릭을 써서 (캔버스 뒤에 표시될) 대체 내용을 사파리가 보여주지 않도록 해야 하며, 이 내용이 IE에서는 보이도록 다시 CSS 트릭을 써야 합니다. <b>Todo: get hixie to put the CSS bits in</b>
</p>
<h3 name=".EC.B6.94.EA.B0.80.EC.A0.81.EC.9D.B8_.EA.B8.B0.EB.8A.A5"> 추가적인 기능 </h3>
<h4 name=".EC.9B.B9_.EB.82.B4.EC.9A.A9.EC.9D.84_.EC.BA.94.EB.B2.84.EC.8A.A4.EC.97.90_.EB.A0.8C.EB.8D.94.EB.A7.81.ED.95.98.EA.B8.B0"> 웹 내용을 캔버스에 렌더링하기 </h4>
<div class="note">이 기능은 크롬 권한을 가진 코드만 사용할 수 있습니다. 보통 HTML 페이지에서는 허용되지 않습니다.</div>
<p>모질라의 <code>canvas</code>는 <code>drawWindow</code> 메소드로 확장할 수 있습니다. 이 메소드는 DOM <code>window</code>의 현재 내용을 캔버스에 그립니다. 예를 들어서,
</p>
<pre class="eval">ctx.drawWindow(window, 0, 0, 100, 200, "rgb(0,0,0)");
</pre>
<p>이 코드는 현재 창의 내용을 캔버스의 (0,0)-(100,200) 사각형에 검은 바탕으로 그리는데, 좌표는 뷰포트(viewport)의 왼쪽 위 모서리에 상대적입니다. 색깔을 "rgba(0,0,0,0)"으로 지정하면 내용은 투명 배경으로 그려질 것입니다 (느려질 수 있음).
</p><p>이 방법을 사용하면, 임의의 내용(CSS 스타일이 적용된 HTML 텍스트나 SVG)이 들어 있는 숨겨진 IFRAME을 채우고 캔버스에 그릴 수 있습니다. 현재 변환에 따라 확대 및 축소하거나, 회전도 가능합니다.
</p><p>Ted Mielczarek의 <a class="external" href="http://ted.mielczarek.org/code/mozilla/tabpreview/">tab preview</a> 확장기능은 이 기법을 크롬에서 웹 페이지의 축소 이미지를 만드는 데 사용했고, 소스 코드는 참고를 위해 공개되어 있습니다.
</p>
<h3 name=".EA.B0.99.EC.9D.B4_.EB.B3.B4.EA.B8.B0"> 같이 보기 </h3>
<ul><li> <a href="ko/Canvas_tutorial">캔버스 길라잡이</a>
</li><li> <a class="external" href="http://www.whatwg.org/specs/web-apps/current-work/#scs-dynamic">WHATWG 명세</a>
</li><li> <a class="external" href="http://developer.apple.com/documentation/AppleApplications/Reference/SafariJSRef/Classes/Canvas.html">Apple Canvas 문서</a>
</li><li> <a class="external" href="http://weblogs.mozillazine.org/roc/archives/2005/05/rendering_web_p.html">웹 페이지 축소 이미지를 렌더링하기</a>
</li><li> 몇 가지 <a href="Special:Tags?tag=Canvas_examples&amp;language=ko">예제</a>:
<ul><li> <a href="ko/A_Basic_RayCaster">기초적인 레이캐스팅</a>
</li><li> <a class="external" href="http://awordlike.com/">The Lightweight Visual Thesaurus</a>
</li><li> <a class="external" href="http://caimansys.com/painter/">Canvas Painter</a>
</li></ul>
</li><li> <a href="Special:Tags?tag=HTML:Canvas&amp;language=ko">등등...</a>
</li></ul>
{{ wiki.languages( { "fr": "fr/Dessiner_avec_canvas", "ja": "ja/Drawing_Graphics_with_Canvas", "ko": "ko/Drawing_Graphics_with_Canvas", "pl": "pl/Rysowanie_grafik_za_pomoc\u0105_Canvas" } ) }}
Revert to this revision