캔버스(canvas)를 이용한 도형 그리기 - Web API

캔버스(canvas)를 이용한 도형 그리기
=======================

* « 이전
* 다음 »

앞서 캔버스 환경 설정(canvas environment)을 완료 하였다면, 이제 어떻게 캔버스에 그릴수 있는지에 대하여 자세하게 알아봅시다. 이 글을 끝내고 난 후, 여러분은 어떻게 사각형, 삼각형, 선, 아치, 곡선 등 의 기본적인 도형을 그릴수 있는지 익히실 수 있을 것 입니다. 캔버스 위에 물체를 그릴 때에는 path를 사용하는것이 필수적이므로 우리는 이것이 어떻게 사용되는지 볼 것입니다.

그리드
---

![](/ko/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes/canvas_default_grid.png)드로잉을 시작 하기에 앞서, 캔버스 그리드 혹은 좌표공간 (**coordinate space)** 에 대하여 이야기 해보겠습니다. 이전 페이지에서 이야기 했던 HTML 골격(skeleton)는 가로 세로 각각 150 픽셀의 캔버스 요소를 가지고 있습니다. 오른쪽에 보시면, 캔버스와 기본 그리드가 놓인것을 보실 수 있습니다. 기본적으로 그리드의 1단위는 캔버스의 1픽셀과 같습니다. 이 그리드의 원점은 좌측상단의 (0,0) 입니다. 모든 요소들은 이 원점을 기준으로 위치됩니다. 그렇기 때문에, 파란 사각형의 좌측상단은 왼쪽에서 x 픽셀, 위에서 y 픽셀 떨어진 것이라 볼 수 있고, 이 사각형의 좌표는 (x,y)가 됩니다. 이 튜토리얼 후반부에서 어떻게 원점을 이동하며, 그리드를 회전하고 같은 비율로 확대/축소할 수 있는지 살펴볼 것이지만, 지금은 기본에 충실하도록 합시다.

직사각형 그리기
--------

SVG 와는 다르게, `<canvas>`는 오직 하나의 원시적인 도형만을 제공합니다. 바로 **직사각형** 입니다. 다른 모든 도형들은 무조건 하나 혹은 하나 이상의 path 와 여러 점으로 이어진 선으로 만들어집니다. 다행히도, 우리는 여러 path drawing 함수(function)들을 통해 아주 어려운 도형들도 그릴수 있습니다.

첫번째로, 직사각형을 봅시다. 캔버스 위에 직사각형을 그리는데에는 세가지 함수(function)가 있습니다:

`fillRect(x, y, width, height)` (en-US)

색칠된 직사각형을 그립니다.

`strokeRect(x, y, width, height)` (en-US)

직사각형 윤곽선을 그립니다.

`clearRect(x, y, width, height)` (en-US)

특정 부분을 지우는 직사각형이며, 이 지워진 부분은 완전히 투명해집니다.

각각의 세 함수는 모두 같은 변수를 가집니다. `x`와 `y`는 캔버스의 좌측상단에서 사각형의 (원점으로부터 상대적인) 위치를 뜻하며, `width` 와 `height`는 사각형의 크기를 뜻하게 됩니다.

전 페이지에서 보여드렸던 `draw()` 함수(function)를 이용하여 위의 세가지 함수를 아래의 예제에 적용해 보았습니다.

### 직사각형 도형 예제
```

<html>
  <body onload="draw();">
    <canvas id="canvas" width="150" height="150"></canvas>
  </body>
</html>
```

```js
function draw() {
  var canvas = document.getElementById("canvas");
  if (canvas.getContext) {
    var ctx = canvas.getContext("2d");

    ctx.fillRect(25, 25, 100, 100);
    ctx.clearRect(45, 45, 60, 60);
    ctx.strokeRect(50, 50, 50, 50);
  }
}
```

위 예제의 결과는 다음과 같습니다.

| Screenshot | Live sample |
| --- | --- |
|  |  |

`fillRect()` 함수는 가로 세로 100 픽셀 사이즈의 검정 사각형을 그립니다. 이후 `clearRect()` 함수가 60x60 픽셀의 사각형 크기로 도형 중앙을 지우게 되고, `strokeRect()`은 이 빈 사각형 공간 안에 50x50 픽셀 사이즈의 윤곽선만 있는 사각형을 만들게 됩니다.

다음 페이지에서, 우리는 `clearRect()`를 대신하는 두개의 함수에 대해 살펴보고, 만들어진 도형의 색이나 윤곽선의 스타일을 바꾸는 방법들에 대하여 알아보도록 하겠습니다.

우리가 다음 섹션에서 보게될 path 함수와 다르게 세개의 직사각형 함수는 캔버스에 바로 그릴 수 있습니다.

경로 그리기
------

\*경로(path)\*는 직사각형 이외의 유일한 원시적인(primitive) 도형입니다. 경로는 점들의 집합이며, 선의 한 부분으로 연결되어 여러가지 도형, 곡선을 이루고 두께와 색을 나타내게 됩니다. 경로나 하위 경로(sub-path)는 닫힐 수 있습니다. 경로를 이용하여 도형을 만들 때에는 몇가지 추가적인 단계를 거쳐야 합니다:

1. 경로를 생성합니다.
2. 그리기 명령어 (en-US)를 사용하여 경로상에 그립니다.
3. 경로가 한번 만들어졌다면, 경로를 렌더링 하기 위해서 윤곽선을 그리거나 도형 내부를 채울수 있습니다.

다음은 위의 단계들을 실행하기 위해 사용되는 함수입니다:

`beginPath()` (en-US)

새로운 경로를 만듭니다. 경로가 생성됬다면, 이후 그리기 명령들은 경로를 구성하고 만드는데 사용하게 됩니다.

Path 메소드 (Path methods (en-US))

물체를 구성할 때 필요한 여러 경로를 설정하는데 사용하는 함수입니다.

`closePath()` (en-US)

현재 하위 경로의 시작 부분과 연결된 직선을 추가합니다.

`stroke()` (en-US)

윤곽선을 이용하여 도형을 그립니다.

`fill()` (en-US)

경로의 내부를 채워서 내부가 채워진 도형을 그립니다.

경로를 만들기 위한 첫번째 단계는 `beginPath()` 메소드를 사용하는 것 입니다. 내부적으로, 경로는 도형을 이루는 하위경로(선, 아치 등)들의 집합으로 이루어져있습니다. 이 메소드가 호출될 때 마다, 하위 경로의 모음은 초기화되며, 우리는 새로운 도형을 그릴 수 있게 됩니다.

**참고:** 현재 열린 path가 비어있는 경우 ( `beginPath()` 메소드를 사용한 직 후, 혹은캔버스를 새로 생성한 직후), 첫 경로 생성 명령은 실제 동작에 상관 없이 `moveTo()`로 여겨지게 됩니다. 그렇기 때문에 경로를 초기화한 직후에는 항상 명확하게 시작 위치를 설정해 두는것이 좋습니다.

두번째 단계는 실제로 경로가 그려지는 위치를 설정하는 메소드를 호출하는 것 입니다. 이 내용에 대해서는 곧 보실수 있습니다.

세번째 단계는 선택사항으로 `closePath()` 메소드를 호출하는 것 입니다. 이 메소드는 현재 점 위치와 시작점 위치를 직선으로 이어서 도형을 닫습니다. 이미 도형이 닫혔거나 한 점만 존재한다면, 이 메소드는 아무것도 하지 않습니다.

**참고:** `fill()` 메소드 호출 시, 열린 도형은 자동으로 닫히게 되므로 `closePath()`메소드를 호출하지 않아도 됩니다. 이것은 `stroke()` 메소드에는 **적용되지 않습니다**.

### 삼각형 그리기

예를 들어, 삼각형을 그리기 위한 코드는 다음과 같습니다:
```

<html>
  <body onload="draw();">
    <canvas id="canvas" width="100" height="100"></canvas>
  </body>
</html>
```

```js
function draw() {
  var canvas = document.getElementById("canvas");
  if (canvas.getContext) {
    var ctx = canvas.getContext("2d");

    ctx.beginPath();
    ctx.moveTo(75, 50);
    ctx.lineTo(100, 75);
    ctx.lineTo(100, 25);
    ctx.fill();
  }
}
```

위 코드의 실행 결과는 다음과 같습니다:

| Screenshot | Live sample |
| --- | --- |
|  |  |

### 펜(pen) 이동하기

가장 유용한 함수중에 실제로 어떤 것도 그리지 않지만 위에서 언급한 경로의 일부가 되는 `moveTo()` 함수가 있습니다. 이는 펜이나 연필을 종이위에서 들어 옆으로 옮기는것이라고 보시면 됩니다.

`moveTo(x, y)` (en-US)

펜을 x와 y 로 지정된 좌표로 옮깁니다.

캔버스가 초기화 되었거나 `beginPath()` 메소드가 호출되었을 때, 특정 시작점 설정을 위해 `moveTo()` 함수를 사용하는것이 좋습니다. 또한 `moveTo()` 함수는 연결되지 않은 경로를 그리는데에도 사용 할 수 있습니다. 아래의 스마일 아이콘을 봅시다.

코드 snippet을 사용해하여 직접 시도하여 보세요. 앞에서 보았던 `draw()` 함수(function)를 붙혀넣기 해 보세요.
```

<html>
  <body onload="draw();">
    <canvas id="canvas" width="150" height="150"></canvas>
  </body>
</html>
```

```js
function draw() {
  var canvas = document.getElementById("canvas");
  if (canvas.getContext) {
    var ctx = canvas.getContext("2d");

    ctx.beginPath();
    ctx.arc(75, 75, 50, 0, Math.PI \* 2, true); // Outer circle
    ctx.moveTo(110, 75);
    ctx.arc(75, 75, 35, 0, Math.PI, false); // Mouth (clockwise)
    ctx.moveTo(65, 65);
    ctx.arc(60, 65, 5, 0, Math.PI \* 2, true); // Left eye
    ctx.moveTo(95, 65);
    ctx.arc(90, 65, 5, 0, Math.PI \* 2, true); // Right eye
    ctx.stroke();
  }
}
```

결과는 다음과 같습니다:

| Screenshot | Live sample |
| --- | --- |
|  |  |

`moveTo()`를 사용한 코드라인을 지우면 연결된 선들을 확인 할 수 있습니다

**참고:** `arc()` function에 대하여 더 알아보고 싶다면 아래의 Arcs 를 확인하세요.

### 선

직선을 그리기 위해서는 `lineTo()` 메소드를 사용할 수 있습니다.

`lineTo(x, y)` (en-US)

현재의 드로잉 위치에서 x와 y로 지정된 위치까지 선을 그립니다.

이 메소드는 선의 끝점의 좌표가 되는 x와 y의 두개의 인자가 필요합니다. 시작점은 이전에 그려진 경로에 의해 결정 되며, 이전 경로의 끝점이 다음 그려지는 경로의 시작점이 됩니다. 또한 시작점은 `moveTo()` 메소드를 통해 변경될 수 있습니다.

아래의 예시는 하나의 두 삼각형 (윤곽선 삼각형, 색칠된 삼각형)을 그립니다.
```

<html>
  <body onload="draw();">
    <canvas id="canvas" width="150" height="150"></canvas>
  </body>
</html>
```

```js
function draw() {
  var canvas = document.getElementById("canvas");
  if (canvas.getContext) {
    var ctx = canvas.getContext("2d");

    // Filled triangle
    ctx.beginPath();
    ctx.moveTo(25, 25);
    ctx.lineTo(105, 25);
    ctx.lineTo(25, 105);
    ctx.fill();

    // Stroked triangle
    ctx.beginPath();
    ctx.moveTo(125, 125);
    ctx.lineTo(125, 45);
    ctx.lineTo(45, 125);
    ctx.closePath();
    ctx.stroke();
  }
}
```

새로운 경로를 지정하기 위해 `beginPath()` 메소드를 먼저 실행합니다. 그 다음 `moveTo()` 메소드를 가지고 시작점을 원하는 위치로 새롭게 지정 해 줍니다. 다음은, 두선을 그어 삼각형의 두 면을 그려줍니다.

| Screenshot | Live sample |
| --- | --- |
|  |  |

여러분은 채워진 삼각형과 윤곽선 삼각형의 차이를 확인 하셨을 겁니다. 위에 언급했던 것 처럼, 경로가 채워지게 되면 그 도형은 자동으로 닫히게 되지만 윤곽선 삼각형에서는 그렇지 않기 때문입니다. 만약에 `closePath()` 메소드를 윤곽선 삼각형 코드에서 지운다면, 오직 두 선만 그려지게 되며 완벽한 삼각형으로 만들어지지 않습니다.

### 호(arc)

호나 원을 그리기위해서는 `arc()` 혹은 `arcTo()` 메소드를 사용합니다..

`arc(x, y, radius, startAngle, endAngle, anticlockwise)` (en-US)

*(x, y)* 위치에 원점을 두면서, 반지름 r을 가지고, *startAngle* 에서 시작하여 *endAngle* 에서 끝나며 주어진 *anticlockwise* 방향으로 향하는 (기본값은 시계방향 회전) 호를 그리게 됩니다.

`arcTo(x1, y1, x2, y2, radius)` (en-US)

주어진 제어점들과 반지름으로 호를 그리고, 이전 점과 직선으로 연결합니다.

`arc` 메소드의 여섯개의 매개변수에 대하여 좀 더 자세하게 알아봅시다: `x` 와 `y`는 호를 그릴 때 필요한 원점 좌표입니다. 반지름(`radius`) 은 말 그대로 호의 반지름을 뜻합니다. `startAngle` 및 `endAngle` 매개 변수는 원의 커브를 따라 호의 시작점과 끝점을 라디안 단위로 정의합니다. 이 변수들은 x축을 기준으로 계산됩니다. Boolean 값을 가지는 `anticlockwise` 변수는 `true`일 때 호를 반시계 방향으로 그리게 되며, 그렇지 않을 경우에는 시계 방향으로 그리게 됩니다.

**참고:** `arc` 함수에서 각도는 각이 아닌 라디안 값을 사용합니다. 각도를 라디안으로 바꾸려면 다음의 JavaScript(JavaScript) 코드를 사용하실 수 있습니다: `radians = (Math.PI/180)*degrees`.

다음의 예제는 우리가 이제껏 봐 왔던 예제들 보다 약간 더 복잡합니다. 이 예제는 12가지의 다양한 각도로 채워진 각기 다른 호를 그립니다.

두개의 `for` loops은 루프를 통해 호(arc)들의 행과 열을 읽기 위해 사용되었습니다. `beginPath()`를 사용해 각 호의 새로운 경로를 만듭니다. 코드 내에서, 각각의 매개변수들을 명확하게 보여주기 위해 변수로 정의 하였지만, 실제로 사용할때 꼭 필요한 것은 아닙니다.

`x`와 `y` 좌표는 충분히 명확하게 표기되어야 합니다. `radius` 와 `startAngle`은 고정되어 있습니다. `endAngle`는 처음 180도 (반원) 에서 시작하고 이후 매번 90도씩 증가하다가 마지막 열에서 완벽한 원을 그립니다.

`clockwise` 매개 변수를 지정한 결과로 첫번째와 세번째 줄은 시계방향으로 원호들이 그려졌고 두번째와 네번째 줄에는 반시계방향의 원호들이 그려졌습니다. 마지막으로 `if` 문은 위 반쪽이 윤곽선으로, 아래 반쪽이 색으로 채워진 호들을 만들어 냅니다.

**참고:** 이 예제는 다른 예제들 보다 더 큰사이즈의 캔버스가 필요합니다: 150 x 200 픽셀
```

<html>
  <body onload="draw();">
    <canvas id="canvas" width="150" height="200"></canvas>
  </body>
</html>
```

```js
function draw() {
  var canvas = document.getElementById('canvas');
  if (canvas.getContext) {
    var ctx = canvas.getContext('2d');

    for (var i = 0; i < 4; i++) {
      for (var j = 0; j < 3; j++) {
        ctx.beginPath();
        var x = 25 + j \* 50; // x coordinate
        var y = 25 + i \* 50; // y coordinate
        var radius = 20; // Arc radius
        var startAngle = 0; // Starting point on circle
        var endAngle = Math.PI + (Math.PI \* j) / 2; // End point on circle
        var anticlockwise = i % 2 == 0 ? false : true; // clockwise or anticlockwise

        ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);

        if (i > 1) {
          ctx.fill();
        } else {
          ctx.stroke();
        }
      }
    }
  }
}
```

| Screenshot | Live sample |
| --- | --- |
|  |  |

### 베지어(Bezier) 곡선과 이차(Quadratic )곡선

다음 경로 타입은 베지어 곡선 (Bézier curves)으로, 삼차(cubic)와 이차(quadric) 변수가 모두 가능합니다. 이 타입은 대게 복잡한 유기체적 형태 (organic shape)를 그리는데 사용됩니다.

`quadraticCurveTo(cp1x, cp1y, x, y)` (en-US)

`cp1x` 및 `cp1y`로 지정된 제어점을 사용하여 현재 펜의 위치에서 `x`와 `y`로 지정된 끝점까지 이차 베지어 곡선을 그립니다.

`bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)` (en-US)

(`cp1x`, `cp1y`) 및 (cp2x, cp2y)로 지정된 제어점을 사용하여 현재 펜 위치에서 `x` 및 `y`로 지정된 끝점까지 삼차 베지어 곡선을 그립니다.

![](/ko/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes/canvas_curves.png)오른쪽의 사진은 두 곡선의 차이를 가장 잘 설명해주고 있습니다. 이차 베지에 곡선은 시작점과 끝점 (파란색 점) 그리고 하나의 **제어점** (control point, 빨간 점으로 표시)을 가지고 있지만, 삼차 베지에 곡선은 두개의 제어점을 사용하고 있습니다.

두 메소드에 모두 사용되는 `x`와 `y` 변수는 모두 끝점의 좌표를 나타냅니다. 첫번째 제어점은 `cp1x` 와 `cp1y` 좌표로, 두번째 제어점은 `cp2x` 와 `cp2y` 좌표로 표시되었습니다.

이차 및 삼차 베지어 곡선을 사용하는 것은 매우 어려울 수 있습니다. Adobe Illustrator와 같은 벡터 드로잉 소프트웨어와는 달리, 우리는 현재 수행중인 작업에 대해 직접적인 시각적 피드백을 받을 수 없기 때문입니다. 이런 점은 복잡한 모양을 그리기 어렵도록 합니다. 다음 예제에서 우리는 간단한 유기체적 형태만 그리도록 하겠지만, 여러분이 연습과 시간을 투자 하신다면, 이후에 더욱 복잡한 도형을 그릴수 있게 될 것입니다.

이 예제는 아주 어려운 점은 없습니다. 두 경우 모두 연속된 곡선이 그려지면서 최종 모양이 완성됩니다.

#### 이차 베지에 곡선(Quadratic Bezier curves)

이 예제는 여러개의 이차 베지어 곡선을 이용해 말풍선을 만들어 냅니다.
```

<html>
  <body onload="draw();">
    <canvas id="canvas" width="150" height="150"></canvas>
  </body>
</html>
```

```js
function draw() {
  var canvas = document.getElementById("canvas");
  if (canvas.getContext) {
    var ctx = canvas.getContext("2d");

    // Quadratric curves example
    ctx.beginPath();
    ctx.moveTo(75, 25);
    ctx.quadraticCurveTo(25, 25, 25, 62.5);
    ctx.quadraticCurveTo(25, 100, 50, 100);
    ctx.quadraticCurveTo(50, 120, 30, 125);
    ctx.quadraticCurveTo(60, 120, 65, 100);
    ctx.quadraticCurveTo(125, 100, 125, 62.5);
    ctx.quadraticCurveTo(125, 25, 75, 25);
    ctx.stroke();
  }
}
```

| Screenshot | Live sample |
| --- | --- |
|  |  |

#### 삼차 베지어 곡선 (Cubic Bezier curves)

이 예제는 삼차 곡선을 이용하여 하트를 그립니다.
```

<html>
  <body onload="draw();">
    <canvas id="canvas" width="150" height="150"></canvas>
  </body>
</html>
```

```js
function draw() {
  var canvas = document.getElementById("canvas");
  if (canvas.getContext) {
    var ctx = canvas.getContext("2d");

    // Cubic curves example
    ctx.beginPath();
    ctx.moveTo(75, 40);
    ctx.bezierCurveTo(75, 37, 70, 25, 50, 25);
    ctx.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5);
    ctx.bezierCurveTo(20, 80, 40, 102, 75, 120);
    ctx.bezierCurveTo(110, 102, 130, 80, 130, 62.5);
    ctx.bezierCurveTo(130, 62.5, 130, 25, 100, 25);
    ctx.bezierCurveTo(85, 25, 75, 37, 75, 40);
    ctx.fill();
  }
}
```

| Screenshot | Live sample |
| --- | --- |
|  |  |

### 직사각형

직사각형을 캔버스에 직접 그리는 직사각형 그리기에서 본 세 가지 메소드 외에 `rect()` 메소드도 있습니다. 이 메소드는 현재 열린 패스에 직사각형 경로를 추가합니다.

`rect(x, y, width, height)` (en-US)

좌측상단이 (x, y)이고 폭과 높이가 `width`와 `height`인 직사각형을 그립니다.

이 메소드가 실행되기 전에, (x,y) 매개변수를 가진 `moveTo()` 메소드가 자동으로 호출됩니다. 즉, 현재의 펜위치가 자동으로 기본 좌표로 초기화 됩니다.

### 조합하기

이제까지 이 페이지의 예제들은 각각의 도형마다 하나의 path 함수를 가지고 있었습니다. 하지만 도형을 만드는데에 사용되는 경로의 종류와 개수는 제한이 없습니다. 그렇기 때문에 이 마지막 예제에서는 모든 경로 함수를 합쳐 여러 게임 캐릭터들을 그려보도록 하겠습니다.
```

<html>
  <body onload="draw();">
    <canvas id="canvas" width="150" height="150"></canvas>
  </body>
</html>
```

```js
function draw() {
  var canvas = document.getElementById("canvas");
  if (canvas.getContext) {
    var ctx = canvas.getContext("2d");

    roundedRect(ctx, 12, 12, 150, 150, 15);
    roundedRect(ctx, 19, 19, 150, 150, 9);
    roundedRect(ctx, 53, 53, 49, 33, 10);
    roundedRect(ctx, 53, 119, 49, 16, 6);
    roundedRect(ctx, 135, 53, 49, 33, 10);
    roundedRect(ctx, 135, 119, 25, 49, 10);

    ctx.beginPath();
    ctx.arc(37, 37, 13, Math.PI / 7, -Math.PI / 7, false);
    ctx.lineTo(31, 37);
    ctx.fill();

    for (var i = 0; i < 8; i++) {
      ctx.fillRect(51 + i \* 16, 35, 4, 4);
    }

    for (i = 0; i < 6; i++) {
      ctx.fillRect(115, 51 + i \* 16, 4, 4);
    }

    for (i = 0; i < 8; i++) {
      ctx.fillRect(51 + i \* 16, 99, 4, 4);
    }

    ctx.beginPath();
    ctx.moveTo(83, 116);
    ctx.lineTo(83, 102);
    ctx.bezierCurveTo(83, 94, 89, 88, 97, 88);
    ctx.bezierCurveTo(105, 88, 111, 94, 111, 102);
    ctx.lineTo(111, 116);
    ctx.lineTo(106.333, 111.333);
    ctx.lineTo(101.666, 116);
    ctx.lineTo(97, 111.333);
    ctx.lineTo(92.333, 116);
    ctx.lineTo(87.666, 111.333);
    ctx.lineTo(83, 116);
    ctx.fill();

    ctx.fillStyle = "white";
    ctx.beginPath();
    ctx.moveTo(91, 96);
    ctx.bezierCurveTo(88, 96, 87, 99, 87, 101);
    ctx.bezierCurveTo(87, 103, 88, 106, 91, 106);
    ctx.bezierCurveTo(94, 106, 95, 103, 95, 101);
    ctx.bezierCurveTo(95, 99, 94, 96, 91, 96);
    ctx.moveTo(103, 96);
    ctx.bezierCurveTo(100, 96, 99, 99, 99, 101);
    ctx.bezierCurveTo(99, 103, 100, 106, 103, 106);
    ctx.bezierCurveTo(106, 106, 107, 103, 107, 101);
    ctx.bezierCurveTo(107, 99, 106, 96, 103, 96);
    ctx.fill();

    ctx.fillStyle = "black";
    ctx.beginPath();
    ctx.arc(101, 102, 2, 0, Math.PI \* 2, true);
    ctx.fill();

    ctx.beginPath();
    ctx.arc(89, 102, 2, 0, Math.PI \* 2, true);
    ctx.fill();
  }
}

// A utility function to draw a rectangle with rounded corners.

function roundedRect(ctx, x, y, width, height, radius) {
  ctx.beginPath();
  ctx.moveTo(x, y + radius);
  ctx.lineTo(x, y + height - radius);
  ctx.arcTo(x, y + height, x + radius, y + height, radius);
  ctx.lineTo(x + width - radius, y + height);
  ctx.arcTo(x + width, y + height, x + width, y + height - radius, radius);
  ctx.lineTo(x + width, y + radius);
  ctx.arcTo(x + width, y, x + width - radius, y, radius);
  ctx.lineTo(x + radius, y);
  ctx.arcTo(x, y, x, y + radius, radius);
  ctx.stroke();
}
```

결과 이미지는 다음과 같습니다:

| Screenshot | Live sample |
| --- | --- |
|  |  |

이 예제는 보기보다 아주 간단하기 때문에 자세한 설명은 생략하겠습니다. 알아 두어야할 가장 중요한 부분은 `fillStyle` 코드와 사용된 유틸리티 함수 (`roundedRect()` 부분) 입니다. 유틸리티 함수를 사용하게 되면, 사용해야 할 코드의 양과 복잡함을 줄여주는데 도움을 줍니다.

이 튜토리얼에서 나중에 `fillStyle`에 대하여 조금 더 자세하게 알아보도록 하겠지만, 지금은 경로의 채우는 색을 기본값(흑백)에서 바꾸었다가 다시 기본값으로 바꾸는 정도로만 사용하였습니다.

Path2D 오브젝트 (Path2D objects)
----------------------------

마지막 예제에서 보았 듯이, 캔버스에 객체를 그리는 일련의 경로와 그리기 명령이 있을 수 있습니다. 코드를 단순화하고 성능을 향상시키기 위해 최근 버전의 브라우저에서 사용할 수있는 `Path2D` (en-US) 객체를 사용하여 이러한 드로잉 명령을 캐시하거나 기록 할 수 있습니다. 이로써 여러분은 경로를 빠르게 다시 실행 시킬 수 있습니다.

어떻게 `Path2D` object를 생성 할 수 있는지 확인해 봅시다:

`Path2D()` (en-US)

**`Path2D()`** 생성자는 새로운 `Path2D` 객체를 반환합니다. 선택적으로 다른 경로를 인수로 받거나(복사본을 생성), SVG 경로 데이터로 구성된 문자열을 받아서 객체로 반환합니다.

```js
new Path2D(); // empty path object
new Path2D(path); // copy from another Path2D object
new Path2D(d); // path from SVG path data
```

`moveTo`, `rect`, `arc` 혹은 `quadraticCurveTo` 등과 같은 모든 경로 메소드 (path methods (en-US))들은 `Path2D` 객체에서 사용 가능합니다.

`Path2D` API는 또한 `addPath` 메소드를 사용하여 경로를 결합하는 방법을 추가합니다. 예를 들자면, 여러 요소를 사용하는 오브젝트를 만들 때 유용하게 사용 될 수 있습니다.

`Path2D.addPath(path [, transform])` (en-US)

옵션으로 변환 행렬(transformation matrix)을 사용하여 현재 경로에 경로를 추가합니다.

### Path2D 예제

이 예제에서는, 직사각형과 원을 만들어 볼 것입니다. 나중에 사용할 것을 고려하여, 두 도형 모두 `Path2D` object로 저장 될 것입니다. 새로운 버전의 `Path2D` API에서는 여러 메소드들이 지금 사용하고있는 path가 아닌 `Path2D` object를 옵션으로 선택하여 사용 할 수 있도록 업데이트 되었습니다. 아래의 예제에서 보시면, `stroke` 와 `fill` 메소드가 오브젝트를 캔버스 위에 그리도록 path 변수와 함께 사용되었습니다.
```

<html>
  <body onload="draw();">
    <canvas id="canvas" width="130" height="100"></canvas>
  </body>
</html>
```

```js
function draw() {
  var canvas = document.getElementById("canvas");
  if (canvas.getContext) {
    var ctx = canvas.getContext("2d");

    var rectangle = new Path2D();
    rectangle.rect(10, 10, 50, 50);

    var circle = new Path2D();
    circle.moveTo(125, 35);
    circle.arc(100, 35, 25, 0, 2 \* Math.PI);

    ctx.stroke(rectangle);
    ctx.fill(circle);
  }
}
```

| Screenshot | Live sample |
| --- | --- |
|  |  |

### SVG paths 사용하기

새로운 캔버스 path2D API 또다른 강력한 특징 중 하나는, 캔버스의 path를 초기화 하기 위해 SVG path data를 사용한다는 것입니다. 이는 path 데이터를 이동시키며, SVG와 canvas 에서 재사용 할 수 있도록 해줍니다.

path는 (`M10 10`) 점으로 이동한 다음, 수평하게 오른쪽으로 으로 80 포인트 (`h 80`) 만큼 이동합니다. 이후 수직으로 80포인트 (v 80) 내려간 다음, 80 포인트 왼쪽으로 (`h -80`) 수평하게 이동하고 다시 시작점 (`z`)으로 돌아갑니다. 예시는 이곳 (en-US)( `Path2D` constructor )에서 확인하실 수 있습니다.

```js
var p = new Path2D("M10 10 h 80 v 80 h -80 Z");
```

* « 이전
* 다음 »


Finale - Web API

Finale
======

* « 이전

축하합니다 Canvas 튜토리얼을 모두 끝마쳤습니다! 이 정보는 웹에서 더 나은 2D 그래픽을 만드는데 도움이 됩니다.

더 많은 예시와 튜토리얼
-------------

이 사이트에는 다양한 데모와 canvas에 대한 추가 설명이 있다.

Codepen.io

프론트 엔드 개발자 playground 및 브라우저의 코드 편집기.

HTML5 Canvas Tutorials

대부분 canvas API의 예시

Game development

게임은 가장 인기있는 컴퓨터 활동 중 하나이다. 어떤 표준 규격의 웹 브라우저에서도 실행할 수 있는 더 나은 게임을 개발할 수 있도록 하기 위한 신기술이 끊임없이 등장하고 있다.

기타 웹API
-------

이 API들는 canvas 및 그래픽으로 작업할 때 유용하다.

WebGL

3D를 포함한 복잡한 그래픽 렌더링을 위한 고급 API.

SVG

확장 가능한 Vector Graphics는 이미지를 벡터(선)과 형태의 집합으로 묘사하여 이미지를 그리는 크기에 상관없이 원활하게 확장할 수 있다.

Web Audio

Web Audio API는 개발자들이 오디오 소스 선택, 오디오에 효과 추가, 오디오 시각화 생성, 공간 효과 적용(예: 패닝)등을 할 수 있도록 웹 상에서 오디오를 제어하기 위한 다양하고 좋은 시스템을 제공한다.

문의사항
----

Stack Overflow

"canvas"가 태그된 문의사항.

Comments about this tutorial – the MDN documentation community

이 튜토리얼에 대해 의견이 있으시거나 저희에게 감사를 표하고 싶다면 언제든지 연락해주세요!

* « 이전


텍스트 그리기 - Web API

텍스트 그리기
=======

* « 이전
* 다음 » (en-US)

이전 챕터에서 스타일과 컬러를 적용하는 방법에 대해서 보았고 이번에는 캔버스에 텍스트를 그리는 방법에 대해서 볼 예정입니다.

텍스트 그리기
-------

캔버스 렌더링 컨텍스트(canvas rendering context)는 텍스트를 렌더링하는 두 가지 방법을 제공합니다.

`fillText(text, x, y [, maxWidth])` (en-US)

주어진 (x, y) 위치에 주어진 텍스트를 채웁니다. 최대 폭(width)은 옵션 값 입니다.

`strokeText(text, x, y [, maxWidth])` (en-US)

주어진 (x, y) 위치에 주어진 텍스트를 칠(stroke)합니다. 최대 폭(width)은 옵션 값 입니다.

### `fillText` 예제

텍스트는 현재의 `fillStyle`을 사용하여 채워집니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");
  ctx.font = "48px serif";
  ctx.fillText("Hello world", 10, 50);
}
```

```
<canvas id="canvas" width="300" height="100"></canvas>
```

```
draw();
```

### `strokeText` 예제

텍스트는 현재의 `strokeStyle`을 이용하여 채워집니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");
  ctx.font = "48px serif";
  ctx.strokeText("Hello world", 10, 50);
}
```

```
<canvas id="canvas" width="300" height="100"></canvas>
```

```
draw();
```

텍스트 스타일 적용하기
------------

위의 예제에서 우리는 이미 텍스트를 기본 사이즈를 키우기 위하여 `font` 프로퍼티를 사용하였습니다. 그리고 캔버스에 표시되는 텍스트를 조정할 수 있는 속성이 더 있습니다.

`font = value` (en-US)

텍스트를 그릴 때 사용되는 현재 텍스트 스타일. 이 문자열은 CSS `font` 프로퍼티와 동일한구문을 사용합니다. 기본값으로 sans-serif의 10px가 설정되어 있습니다.

`textAlign = value` (en-US)

텍스트 정렬 설정. 사용가능한 값: `start`, `end`, `left`, `right`, `center`. 기본 값은 `start` 입니다.

`textBaseline = value` (en-US)

베이스라인 정렬 설정. 사용가능한 값: `top`, `hanging`, `middle`, `alphabetic`, `ideographic`, `bottom`. 기본 값은 `alphabetic` 입니다

`direction = value` (en-US)

글자 방향. 사용가능한 값: `ltr`, `rtl`, `inherit`. 기본 값은 `inherit` 입니다.

만약 CSS를 다뤄보신적이 있다면 이러한 프로퍼티들은 익숙하실겁니다.

다음에 나오는 WHATWG 예제 다이어그램은 `textBaseline`를 이용하여 다양한 베이스라인 설정을 보여줍니다.
 ![The top of the em square is
roughly at the top of the glyphs in a font, the hanging baseline is
where some glyphs like आ are anchored, the middle is half-way
between the top of the em square and the bottom of the em square,
the alphabetic baseline is where characters like Á, ÿ,
f, and Ω are anchored, the ideographic baseline is
where glyphs like 私 and 達 are anchored, and the bottom
of the em square is roughly at the bottom of the glyphs in a
font. The top and bottom of the bounding box can be far from these
baselines, due to glyphs extending far outside the em square.](http://www.whatwg.org/specs/web-apps/current-work/images/baselines.png)

### textBaseline 예제

아래의 코드를 수정하여 캔버스에서 어떻게 바뀌는지 실시간으로 확인해 보세요.

```js
ctx.font = "48px serif";
ctx.textBaseline = "hanging";
ctx.strokeText("Hello world", 0, 100);
```

```
<canvas id="canvas" width="400" height="200" class="playable-canvas"></canvas>
<div class="playable-buttons">
  <input id="edit" type="button" value="Edit" />
  <input id="reset" type="button" value="Reset" />
</div>
<textarea id="code" class="playable-code">
ctx.font = "48px serif";
ctx.textBaseline = "hanging";
ctx.strokeText("Hello world", 0, 100);</textarea
>
```

```
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var textarea = document.getElementById("code");
var reset = document.getElementById("reset");
var edit = document.getElementById("edit");
var code = textarea.value;

function drawCanvas() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  eval(textarea.value);
}

reset.addEventListener("click", function () {
  textarea.value = code;
  drawCanvas();
});

edit.addEventListener("click", function () {
  textarea.focus();
});

textarea.addEventListener("input", drawCanvas);
window.addEventListener("load", drawCanvas);
```

어드밴스드 텍스트 측정
------------

만약 텍스트에대해 조금 더 디테일한 것들을 얻고 싶다면 다음의 메소드를 이용해보세요.

`measureText()` (en-US)

현재 스타일로 특정 텍스트가 그려질 때의 폭, 픽셀 등을 포함하는 `TextMetrics` (en-US) 객체 리턴.

다음의 코드는 어떻게 텍스트를 측정하는 지, 그리고 폭을 구하는 예제입니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");
  var text = ctx.measureText("foo"); // TextMetrics object
  text.width; // 16;
}
```

Gecko 사용시 주의점
-------------

Gecko(Firefox, Firefox OS외 Mozilla 기반의 에플리케이션 렌더링 엔진)에서는 캔버스에 텍스트를 그리기 위한 몇몇의 prefixed APIs (en-US)가 구현되어 있습니다. 그리고 지금은 사용되지 않아 제거되었거나 작동을 보장하지 않는 것들도 있습니다.

* « 이전
* 다음 » (en-US)


기본 애니메이션 - Web API

기본 애니메이션
========

`<canvas>` 요소는 JavaScript로 제어하는 것이므로, 애니메이션도 쉽게 만들 수 있습니다. 복잡한 애니메이션을 만드는 것은 추가 작업이 더 필요하고, 앞으로 그에 대한 페이지도 머지 않아 추가되기를 기대합니다.

도형은 한번 만들어 놓으면 그 모습 그대로 있다는 것이 애니메이션을 만들 때의 가장 큰 제약일 것입니다. 그 도형을 움직이고자 하면 그 도형뿐만이 아니라 그 도형이 그려지기 전에 그려진 모든 것을 다시 그려야 합니다. 복잡한 장면을 다시 그리는 것은 시간도 많이 걸리며, 코드를 실행하는 컴퓨터의 능력에 따라 달라집니다.

기본 애니메이션 단계
-----------

한 장면을 그리려면 아래와 같은 단계를 밟습니다.

1. **캔버스를 비웁니다.**
 그리려는 도형이 (배경 이미지를 만들 때처럼) 캔버스를 가득 채우는 것이 아니라면, 이전에 그려진 모든 도형을 지울 필요가 있습니다. 가장 쉬운 방법은 `clearRect()` 메소드를 사용하는 것입니다.
2. **캔버스 상태를 저장합니다.**
 캔버스 상태에 영향을 주는 (스타일 변경, 모양 변형 등의) 설정값을 바꾸려고 하고, 바뀐 값을 각 장면마다 사용하려고 한다면, 원래 상태를 저장할 필요가 있습니다.
3. **애니메이션할 도형을 그립니다.**
 실제 장면을 그리는 단계입니다.
4. **캔버스 상태를 복원합니다.**
 새로운 장면을 그리기 전에 저장된 상태를 복원합니다.
애니메이션 제어하기
----------

캔버스 메소드를 사용하거나 사용자 함수를 사용하여 캔버스에 도형들을 그립니다. 보통의 경우에서는, 코드 실행이 완료되면 캔버스에 나타나는 결과만을 보게 됩니다. 예를 들어, `for` 반복문 안에서 애니메이션을 실행하는 것은 불가능합니다.

그래서 정해진 시간마다 그리기 함수를 실행하는 방법이 필요한 것입니다. 아래와 같이 애니메이션을 제어하는 두 가지 방법이 있습니다.

### 예정된 변경

정해진 시간마다 특정 함수를 부를 때 사용할 수 있는 `window.setInterval()` (en-US)과 `window.setTimeout()` (en-US) 함수가 있습니다.

**참고:** 알아둘 것: 현재는 애니메이션을 만드는 방법으로 `window.requestAnimationFrame()` 메소드를 추천합니다. 이에 대한 튜토리얼은 곧 업데이트할 것입니다.

`setInterval(function, delay)`

`delay` 밀리세컨드(1,000분의 1초)마다 `function` 함수 반복 실행을 시작합니다.

`setTimeout(function, delay)`

`delay` 밀리세컨드(1,000분의 1초) 경과후, `function` 함수를 실행합니다.

사용자의 제어를 **필요로 하지 않는** 반복 실행에는 `setInterval()` 함수가 알맞을 것입니다.

### 사용자 상호 작용 변경

애니메이션을 제어하는 두번째 방법은 사용자 입력입니다. 게임을 만들려고 한다면, 애니메이션을 제어하기 위해 키보드나 마우스 이벤트를 사용할 수 있을 것입니다. `EventListener`를 설정하여, 사용자와 상호 작용하여 애니메이션 함수를 실행합니다.

사용자 상호 작용이 **필요하다면**, 우리가 만든 애니메이션용 프레임웍(framework)의 최소 기능 버전 (en-US) 또는 최대 기능 버전을 사용할 수 있을 것입니다.

```js
var myAnimation = new MiniDaemon(null, animateShape, 500, Infinity);
```

또는

```js
var myAnimation = new Daemon(null, animateShape, 500, Infinity);
```

아래 예제에서는, 애니메이션을 제어하기 위해 `window.setInterval()` (en-US)을 사용할 것입니다. 페이지 제일 아래쪽에 `window.setTimeout()` (en-US)을 사용한 예제 링크도 있습니다.

#### 태양계 애니메이션

이 예제에서는 달이 지구를 돌고 지구가 태양을 도는 애니메이션을 만듭니다.

```js
var sun = new Image();
var moon = new Image();
var earth = new Image();
function init() {
  sun.src = "canvas\_sun.png";
  moon.src = "canvas\_moon.png";
  earth.src = "canvas\_earth.png";
  setInterval(draw, 100);
}

function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");

  ctx.globalCompositeOperation = "destination-over";
  ctx.clearRect(0, 0, 300, 300); // 캔버스를 비운다

  ctx.fillStyle = "rgba(0,0,0,0.4)";
  ctx.strokeStyle = "rgba(0,153,255,0.4)";
  ctx.save();
  ctx.translate(150, 150);

  // 지구
  var time = new Date();
  ctx.rotate(
    ((2 \* Math.PI) / 60) \* time.getSeconds() +
      ((2 \* Math.PI) / 60000) \* time.getMilliseconds(),
  );
  ctx.translate(105, 0);
  ctx.fillRect(0, -12, 50, 24); // Shadow
  ctx.drawImage(earth, -12, -12);

  // 달
  ctx.save();
  ctx.rotate(
    ((2 \* Math.PI) / 6) \* time.getSeconds() +
      ((2 \* Math.PI) / 6000) \* time.getMilliseconds(),
  );
  ctx.translate(0, 28.5);
  ctx.drawImage(moon, -3.5, -3.5);
  ctx.restore();

  ctx.restore();

  ctx.beginPath();
  ctx.arc(150, 150, 105, 0, Math.PI \* 2, false); // 지구 궤도
  ctx.stroke();

  ctx.drawImage(sun, 0, 0, 300, 300);
}
```

```
<canvas id="canvas" width="300" height="300"></canvas>
```

```
init();
```

| Screenshot | Live sample |
| --- | --- |
|  |  |

#### 시계 애니메이션

이 예제에서는, 현재 시각을 보여주는 움직이는 시계를 만듭니다.

```js
function init() {
  clock();
  setInterval(clock, 1000);
}

function clock() {
  var now = new Date();
  var ctx = document.getElementById("canvas").getContext("2d");
  ctx.save();
  ctx.clearRect(0, 0, 150, 150);
  ctx.translate(75, 75);
  ctx.scale(0.4, 0.4);
  ctx.rotate(-Math.PI / 2);
  ctx.strokeStyle = "black";
  ctx.fillStyle = "white";
  ctx.lineWidth = 8;
  ctx.lineCap = "round";

  // 시계판 - 시
  ctx.save();
  for (var i = 0; i < 12; i++) {
    ctx.beginPath();
    ctx.rotate(Math.PI / 6);
    ctx.moveTo(100, 0);
    ctx.lineTo(120, 0);
    ctx.stroke();
  }
  ctx.restore();

  // 시계판 - 분
  ctx.save();
  ctx.lineWidth = 5;
  for (i = 0; i < 60; i++) {
    if (i % 5 != 0) {
      ctx.beginPath();
      ctx.moveTo(117, 0);
      ctx.lineTo(120, 0);
      ctx.stroke();
    }
    ctx.rotate(Math.PI / 30);
  }
  ctx.restore();

  var sec = now.getSeconds();
  var min = now.getMinutes();
  var hr = now.getHours();
  hr = hr >= 12 ? hr - 12 : hr;

  ctx.fillStyle = "black";

  // 시간 표시 - 시
  ctx.save();
  ctx.rotate(
    hr \* (Math.PI / 6) + (Math.PI / 360) \* min + (Math.PI / 21600) \* sec,
  );
  ctx.lineWidth = 14;
  ctx.beginPath();
  ctx.moveTo(-20, 0);
  ctx.lineTo(80, 0);
  ctx.stroke();
  ctx.restore();

  // 시간 표시 - 분
  ctx.save();
  ctx.rotate((Math.PI / 30) \* min + (Math.PI / 1800) \* sec);
  ctx.lineWidth = 10;
  ctx.beginPath();
  ctx.moveTo(-28, 0);
  ctx.lineTo(112, 0);
  ctx.stroke();
  ctx.restore();

  // 시간 표시 - 초
  ctx.save();
  ctx.rotate((sec \* Math.PI) / 30);
  ctx.strokeStyle = "#D40000";
  ctx.fillStyle = "#D40000";
  ctx.lineWidth = 6;
  ctx.beginPath();
  ctx.moveTo(-30, 0);
  ctx.lineTo(83, 0);
  ctx.stroke();
  ctx.beginPath();
  ctx.arc(0, 0, 10, 0, Math.PI \* 2, true);
  ctx.fill();
  ctx.beginPath();
  ctx.arc(95, 0, 10, 0, Math.PI \* 2, true);
  ctx.stroke();
  ctx.fillStyle = "rgba(0,0,0,0)";
  ctx.arc(0, 0, 3, 0, Math.PI \* 2, true);
  ctx.fill();
  ctx.restore();

  ctx.beginPath();
  ctx.lineWidth = 14;
  ctx.strokeStyle = "#325FA2";
  ctx.arc(0, 0, 142, 0, Math.PI \* 2, true);
  ctx.stroke();

  ctx.restore();
}
```

```
<canvas id="canvas" width="150" height="150"></canvas>
```

```
init();
```

| Screenshot | Live sample |
| --- | --- |
|  |  |

#### 움직이는 파노라마 사진

이 예제에서는, 움직이는 파노라마 사진을 만듭니다. 사용할 그림은 위키피디어(Wikipedia)에서 구한 요세미티 국립공원입니다. 캔버스보다 큰 그림을 사용할 수도 있습니다.

```js
var img = new Image();

// 변수
// 스크롤될 이미지, 방향, 속도를 바꾸려면 변수값을 바꾼다.

img.src = "capitan\_meadows,\_yosemite\_national\_park.jpg";
var CanvasXSize = 800;
var CanvasYSize = 200;
var speed = 30; // 값이 작을 수록 빨라진다
var scale = 1.05;
var y = -4.5; // 수직 옵셋

// 주요 프로그램

var dx = 0.75;
var imgW;
var imgH;
var x = 0;
var clearX;
var clearY;
var ctx;

img.onload = function () {
  imgW = img.width \* scale;
  imgH = img.height \* scale;
  if (imgW > CanvasXSize) {
    x = CanvasXSize - imgW;
  } // 캔버스보다 큰 이미지
  if (imgW > CanvasXSize) {
    clearX = imgW;
  } // 캔버스보다 큰 이미지
  else {
    clearX = CanvasXSize;
  }
  if (imgH > CanvasYSize) {
    clearY = imgH;
  } // 캔버스보다 큰 이미지
  else {
    clearY = CanvasYSize;
  }
  // 캔버스 요소 얻기
  ctx = document.getElementById("canvas").getContext("2d");
  // 새로 그리기 속도 설정
  return setInterval(draw, speed);
};

function draw() {
  // 캔버스를 비운다
  ctx.clearRect(0, 0, clearX, clearY);
  // 이미지가 캔버스보다 작거나 같다면 (If image is <= Canvas Size)
  if (imgW <= CanvasXSize) {
    // 재설정, 처음부터 시작
    if (x > CanvasXSize) {
      x = 0;
    }
    // 추가 이미지 그리기
    if (x > CanvasXSize - imgW) {
      ctx.drawImage(img, x - CanvasXSize + 1, y, imgW, imgH);
    }
  }
  // 이미지가 캔버스보다 크다면 (If image is > Canvas Size)
  else {
    // 재설정, 처음부터 시작
    if (x > CanvasXSize) {
      x = CanvasXSize - imgW;
    }
    // 추가 이미지 그리기
    if (x > CanvasXSize - imgW) {
      ctx.drawImage(img, x - imgW + 1, y, imgW, imgH);
    }
  }
  // 이미지 그리기
  ctx.drawImage(img, x, y, imgW, imgH);
  // 움직임 정도
  x += dx;
}
```

예제에 사용된 `<canvas>`의 크기는 아래와 같다. 캔버스의 너비가 변수 `CanvasXSize`값과 같고, 캔버스의 높이는 변수 `CanvasYSize`값과 같다는 것에 주목하라.

```html
<canvas id="canvas" width="800" height="200"></canvas>
```

#### Live sample

또 다른 예제들
--------

Gartic

다중 사용자 지원 그리기 놀이.

Canvascape

3D 어드벤처 게임 (1인칭 슈팅).

A basic ray-caster

키보드를 사용한 애니메이션 제어 방법에 대한 좋은 예제.

canvas adventure

키보드 제어를 사용하는 또다른 좋은 예제.

An interactive Blob

물방울 갖고 놀기.

Flying through a starfield

별, 동그라미, 네모가 떠다니는 우주를 누벼라.

iGrapher

주식 시장 자료 차트 예제.

이것도 보세요
-------

* JavaScript timers
* `setInterval` – A little framework (en-US)
* JavaScript Daemons Management
* HTMLCanvasElement (en-US)
* « 이전 (en-US)
* 다음 » (en-US)


도형 합성 - Web API

도형 합성
=====

이전 페이지들에서 나온 모든 예제에서, 새로 그리는 도형은 언제나 이미 그려진 도형의 위에 그려졌습니다. 대부분의 상황에서는 이렇게 하는 것이 적절하지만, 도형을 합성하기 위한 순서를 제한하게 되는데, `globalCompositeOperation` 속성을 설정함으로써 이러한 상태를 바꿀 수 있습니다.

`globalCompositeOperation`
--------------------------

기존 도형 뒤에 새로운 도형을 그릴 수 있을 뿐만 아니라, 도형의 일정 영역을 가려 보이지 않게 하거나 캔버스의 특정 부분을 지우는 (`clearRect()` 메소드는 사각형의 영역만을 지우지만, 이같은 제한도 없다.) 등의 효과를 얻을 수도 있습니다.

`globalCompositeOperation = type`

새로운 도형을 그릴 때, 도형 합성 방법을 설정합니다. type은 다음 26종류의 합성 방법 중에서 선택할 수 있습니다.

다음 예제의 코드를 확인하려면 도형 합성 예제를 확인해 주세요.

잘라내기 경로(Clipping path)
----------------------

![](/ko/docs/Web/API/Canvas_API/Tutorial/Compositing/canvas_clipping_path.png) 잘라내기 경로는 다른 캔버스 도형과 비슷하지만, 다른 도형에서 원하지 않는 부분을 가리는 가면과 같은 역할을 합니다. 오른쪽에 있는 그림을 보면 어떤 역할을 하는지 알 수 있을 것입니다. 붉은 별 모양이 잘라내기 경로입니다. 이 경로 밖에 있는 모든 것은 캔버스에 그려지지 않을 것입니다.

잘라내기 경로와 위에서 살펴본 `globalCompositeOperation` 속성을 비교해 보면, `source-in`과 `source-atop`에서 비슷한 효과가 보입니다. 이들과 잘라내기 경로와의 가장 중요한 차이점은, 잘라내기 경로 자체는 캔버스에 전혀 그려지지 않는다는 것입니다. 잘라내기 경로는 제한된 영역 안에서 여러 가지 도형을 그리는 데에 적합합니다.

캔버스에 도형 그리기 (en-US)에서는 `stroke()`과 `fill()` 메소드만을 설명했었는데, `clip()`이라는 세 번째 메소드도 있습니다.

`clip()`

현재 그려지는 경로를 현재 잘라내기 경로로 만듭니다.

경로를 닫기 위해 `closePath()` 대신 `clip()`을 사용하고, 경로를 채우거나 윤곽선을 만드는 대신 잘라내기 경로로 만들 수 있습니다.

`<canvas>` 요소의 초기 설정값으로써, 캔버스는 캔버스와 똑같은 크기의 잘라내기 경로를 가집니다. 크기가 똑같기 때문에 잘라내기 효과는 나타나지 않습니다.

### `clip` 예제

다음 예제에서는 특정 영역의 별들만 보이도록 동그란 모양의 잘라내기 경로를 사용할 것입니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");
  ctx.fillRect(0, 0, 150, 150);
  ctx.translate(75, 75);

  // 동그란 모양의 잘라내기 경로를 생성한다
  ctx.beginPath();
  ctx.arc(0, 0, 60, 0, Math.PI \* 2, true);
  ctx.clip();

  // 배경을 그린다
  var lingrad = ctx.createLinearGradient(0, -75, 0, 75);
  lingrad.addColorStop(0, "#232256");
  lingrad.addColorStop(1, "#143778");

  ctx.fillStyle = lingrad;
  ctx.fillRect(-75, -75, 150, 150);

  // 별을 그린다
  for (var j = 1; j < 50; j++) {
    ctx.save();
    ctx.fillStyle = "#fff";
    ctx.translate(
      75 - Math.floor(Math.random() \* 150),
      75 - Math.floor(Math.random() \* 150),
    );
    drawStar(ctx, Math.floor(Math.random() \* 4) + 2);
    ctx.restore();
  }
}

function drawStar(ctx, r) {
  ctx.save();
  ctx.beginPath();
  ctx.moveTo(r, 0);
  for (var i = 0; i < 9; i++) {
    ctx.rotate(Math.PI / 5);
    if (i % 2 == 0) {
      ctx.lineTo((r / 0.525731) \* 0.200811, 0);
    } else {
      ctx.lineTo(r, 0);
    }
  }
  ctx.closePath();
  ctx.fill();
  ctx.restore();
}
```

```
<canvas id="canvas" width="150" height="150"></canvas>
```

```
draw();
```

코드의 위쪽 몇 줄에서는 배경으로 캔버스 크기와 같은 검은색 네모를 그리고, 가운데로 원점을 옮깁니다. 그런 다음 호를 그리고 `clip()`을 사용하여 동그란 모양의 잘라내기 경로를 생성합니다. 캔버스 상태를 저장할 때 잘라내기 경로도 같이 저장됩니다. 이전의 잘라내기 경로를 보존하려면, 새로운 잘라내기 경로를 만들기 전에 캔버스 상태를 저장하면 됩니다.

잘라내기 경로를 만든 후에 그려지는 모든 것들은, 그 경로의 안쪽에서만 보입니다. 이는 그 다음에 그려지는 선형 그라디언트에서 확실히 볼 수 있습니다. 이렇게 하고 나서, `drawStar()` 함수를 사용하여 위치와 크기가 모두 다른 50개의 별을 그립니다. 이 별들은 잘라내기 경로 안쪽에만 나타납니다.

| Screenshot | Live sample |
| --- | --- |
|  |  |

* « 이전
* 다음 »


캔버스(Canvas) 기본 사용법 - Web API

캔버스(Canvas) 기본 사용법
==================

* « 이전
* 다음 »

`<canvas>` HTML 엘리먼트를 살펴보는 것으로 이 튜토리얼을 시작해 보겠습니다. 이 페이지의 끝에서 캔버스 2D 컨텍스트를 설정하는 방법을 알게되고, 여러분의 브라우저에서 첫 번째 예제를 그리게 될 것입니다.

`<canvas>` 요소
-------------

```html
<canvas id="tutorial" width="150" height="150"></canvas>
```

`<canvas>`는 처음에는 src 및 alt 속성이 없다는 점만 제외하면 `<img>` 요소처럼 보입니다. 실제로 `<canvas>` 요소에는 `width`와 `height`의 두 속성만 있습니다. 이것들은 모두 선택사항이며 DOM 프로퍼티를 사용하여 설정할 수도 있습니다. width 및 height 속성을 지정하지 않으면 캔버스의 처음 너비는 **300 픽셀**이고 높이는 **150 픽셀**입니다. 요소는 CSS에 의해 임의로 크기를 정할 수 있지만 렌더링하는 동안 이미지는 레이아웃 크기에 맞게 크기가 조정됩니다. CSS 크기 지정이 초기 캔버스의 비율을 고려하지 않으면 왜곡되어 나타납니다 .

**참고:** **노트:** 만약 렌더링이 왜곡된 것처럼 보이는 경우 CSS를 사용하지 않고 `<canvas>` 속성에서 `width` 및 `height` 속성을 명시적으로 지정하십시오.

`id` 속성(어트리뷰트)는 `<canvas>` 요소에 국한되지 않는 글로벌HTML 속성 (global HTML attributes )중 하나로, 모든 HTML 요소에 적용 ( `class` 등등)될 수 있습니다. 대체로 항상 `id` 속성을 사용해 주는것이 좋은데, 이는 스크립트 내에서 구분을 쉽게 해 줄 수 있기 때문입니다.

`<canvas>`요소는 일반적인 이미지 (`margin`, `border`, `background`…) 처럼 스타일을 적용시킬 수 있습니다. 하지만 이 방법은 실제 캔버스 위에 그리는 것에는 영향을 끼치지 않습니다. 이 방법이 어떻게 사용되는지는 해당 챕터에서 확인 할 수 있습니다. 캔버스에 스타일링이 따로 지정 되있지 않았다면, 캔버스 스타일은 투명으로 설정되어있습니다.

### 대체 콘텐츠

`<canvas>` 요소는 `<video>`, `<audio>` (en-US) 혹은 `<picture>` (en-US)처럼 `<img>`와는 달리, 인터넷 익스플로러 9 이하의 버전이나 텍스트기반 브라우저 등과 같은, 캔버스를 지원하지 않는 오래된 브라우저들을 위한 대체 컨텐츠를 정의하기 쉽습니다. 여러분은 그러한 브라우저들을 위한 대체 컨텐츠를 제공해야 합니다.

대체 컨텐츠를 제공하는 것은 매우 간단합니다. `<canvas>`태그 안에 대체 컨텐츠를 삽입합니다. `<canvas>`태그 를 지원하지 않는 브라우저는 컨테이너를 무시하고 컨테이너 내부의 대체 콘텐츠를 렌더링 합니다. `<canvas>`를 지원하는 브라우저는 컨테이너 내부의 내용을 무시하고 캔버스를 정상적으로 렌더링합니다.

예를 들어, 캔버스 내용에 대한 텍스트 설명을 제공하거나 동적으로 렌더링 된 내용의 정적 이미지를 제공 할 수 있습니다. 이것은 다음과 같이 보일 수있습니다:

```html
<canvas id="stockGraph" width="150" height="150">
  current stock price: $3.15 +0.15
</canvas>

<canvas id="clock" width="150" height="150">
  <img src="images/clock.png" width="150" height="150" alt="" />
</canvas>
```

사용자에게 캔버스를 지원하는 다른 브라우저를 사용하도록 하는 것은 캔버스를 해석하지 못하는 사용자에게 전혀 도움이 되지 않습니다. 유용한 대체 텍스트나 하위 DOM을 제공하는 것이 캔버스에 더 쉽게 접근할수 있도록 도움이 될 것입니다.

### `</canvas>` 태그 필수

대체 컨텐츠가 제공되는 방식때문에, `<img>` 요소와 달리, `<canvas>` 요소는 닫는 태그(`</canvas>`)가 필요합니다. 닫는 태그가 없다면, 문서의 나머지 부분이 대체 컨텐츠로 간주되고 보이지 않을 것입니다.

대체 컨텐츠가 필요하지 않다면, 단순히 `<canvas id="foo" ...></canvas>`가 모든 미지원 브라우저에서 완전하게 호환됩니다.

렌더링 컨텍스트
--------

`<canvas>` 엘리먼트는 고정 크기의 드로잉 영역을 생성하고 하나 이상의 **렌더링 컨텍스**(**rendering contexts**)를 노출하여, 출력할 컨텐츠를 생성하고 다루게 됩니다. 본 튜토리얼에서는, 2D 렌더링 컨텍스트를 집중적으로 다룹니다. 다른 컨텍스트는 다른 렌더링 타입을 제공합니다. 예를 들어, WebGL은 OpenGL ES 을 기반으로 하는 3D 컨텍스트를 사용합니다.

캔버스는 처음에 비어있습니다. 무언가를 표시하기 위해서, 어떤 스크립트가 랜더링 컨텍스트에 접근하여 그리도록 할 필요가 있습니다. `<canvas>` 요소는 `getContext()` 메서드를 이용해서, 랜더링 컨텍스트와 (렌더링 컨텍스트의) 그리기 함수들을 사용할 수 있습니다. getContext() 메서드는 렌더링 컨텍스트 타입을 지정하는 하나의 파라메터를 가집니다. 본 튜토리얼에서 다루고 있는 2D 그래픽의 경우, `CanvasRenderingContext2D` (en-US)을 얻기위해 `"2d"`로 지정합니다.

```js
var canvas = document.getElementById("tutorial");
var ctx = canvas.getContext("2d");
```

첫 번째 줄의 스크립트는 `document.getElementById()` 메서드를 호출하여 `<canvas>` 요소를 표시할 DOM을 검색합니다. 요소가 있으면 `getContext()` 메서드를 사용하여 드로잉 컨텍스트에 액세스 할 수 있습니다.

지원여부 검사
-------

대체 콘텐츠는 `<canvas>`를 지원하지 않는 브라우저에 표시됩니다. 스크립트 역시 간단하게 `getContext()` 메소드의 존재 여부를 테스트함으로써 프로그래밍 방식으로 지원하는지를 확인할 수 있습니다. 위의 코드 예제는 다음과 같이 될 수 있습니다:

```js
var canvas = document.getElementById("tutorial");

if (canvas.getContext) {
  var ctx = canvas.getContext("2d");
  // drawing code here
} else {
  // canvas-unsupported code here
}
```

템플릿 뼈대
------

다음은 이후의 예제들에서 시작점으로 사용될 수 있는 가장 최소한의 템플릿입니다.

**참고:** **알아두기:** HTML 내에 스크립트(script)를 사용하는것은 좋은 연습 방법이 아닙니다. 다음의 예시에서는 간결하게 나타내기 위해 사용 한 것입니다.

```html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Canvas tutorial</title>
    <script type="text/javascript">
 function draw() {
 var canvas = document.getElementById("tutorial");
 if (canvas.getContext) {
 var ctx = canvas.getContext("2d");
 }
 }
 </script>
    <style type="text/css">
 canvas {
 border: 1px solid black;
 }
 </style>
  </head>
  <body onload="draw();">
    <canvas id="tutorial" width="150" height="150"></canvas>
  </body>
</html>
```

위 스크립트에 `draw()` 함수 문서가 호출되었는데, 이는 문서가 `load` 이벤트를 수신하여 페이지 로딩이 완료될 때 한번 실행됩니다. 이 함수 혹은 이와 유사한 함수는, 페이지가 처음 로딩되는 한, `window.setTimeout()`, `window.setInterval()` (en-US), 혹은 또 다른 이벤트 핸들러 등을 이용하여 호출될 수 있습니다.

다음은 템플릿이 실제로 어떻게 실행되는지를 보여줍니다. 보이는 바와 같이, 초기에 blank 로 보여집니다.

기본 예제
-----

먼저 두 개의 직사각형을 그린 간단한 예제를 보도록하겠습니다. 그 중 하나는 투명도(alpha transparency)를가집니다. 나중에 이 예제가 어떻게 작동하는지 자세히 살펴 보겠습니다.

```html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <script type="application/javascript">
 function draw() {
 var canvas = document.getElementById("canvas");
 if (canvas.getContext) {
 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="150" height="150"></canvas>
  </body>
</html>
```

이 예제는 다음과 같습니다.

| Screenshot | Live sample |
| --- | --- |
|  |  |

* « 이전
* 다음 »


스타일과 색 적용하기 - Web API

스타일과 색 적용하기
===========

* « 이전
* 다음 »

도형 그리기 장에서는 기본 선과 채우기 스타일만 사용했습니다. 여기서 우리는 그리기를 조금 더 매력적으로 만들 수 있는 캔버스 옵션을 살펴볼 것입니다. 그리기에 다른 색상, 선 스타일, 그라디언트, 패턴 및 그림자를 추가하는 방법을 배우게 됩니다.

**참고:** 캔버스 속 내용은 스크린 리더를 사용하는 유저가 접근할 수 없습니다. 만약 캔버스가 순수히 꾸미는 용으로 사용하는 경우, `<canvas>` 시작 태그에 `role="presentation"`을 넣어줘야 합니다. 그렇지 않다면 캔버스 요소에 설명하는 내용을 `aria-label` 속성의 값으로 넣거나, 캔버스 태그 안에 대체 내용을 넣어야 합니다. 캔버스 속 내용은 DOM의 일부가 아니지만, 중첩 대체 내용은 일부가 될 수 있습니다.

색상
--

지금까지는 그리기 메소드만 살펴봤습니다. 도형에 색을 적용하고자 하면, `fillStyle`과 `strokeStyle` 두 가지 중요한 속성을 사용할 수 있습니다.

`fillStyle = color` (en-US)

도형을 채우는 색을 설정합니다.

`strokeStyle = color` (en-US)

도형의 윤곽선 색을 설정합니다.

여기서의 `color`는 CSS의 `<color>`, 그라디언트 객체, 패턴 객체를 뜻합니다. 그라디언트 객체와 패턴 객체는 페이지 아래에서 설명합니다. 윤곽선과 채움 색의 초기 설정값은 검은색입니다. (CSS 색상 값 `#000000`)

**참고:** `strokeStyle` 또는 `fillStyle` 속성을 설정하면, 새로 설정된 값이 앞으로 그려질 도형의 기본 값이 됩니다. 각 도형에 다른 색을 적용하려면 `fillStyle` 또는 `strokeStyle` 속성을 다시 적용해야 합니다.

색의 올바른 값은 CSS3 사양에 나오는 `<color>` 값입니다. 아래에 나오는 색은 모두 한가지 색을 다르게 표현한 것입니다.

```js
// fillStyle에 적용되는 색은 모두 '오렌지'

ctx.fillStyle = "orange";
ctx.fillStyle = "#FFA500";
ctx.fillStyle = "rgb(255, 165, 0)";
ctx.fillStyle = "rgba(255, 165, 0, 1)";
```

### `fillStyle` 예제

이 예제에서 `for` 반복문을 두 번 사용하여 사각형 격자를 그릴 것입니다. 결과는 스크린샷과 같을 것입니다. 결과가 그리 대단한 것은 아닙니다. 각 사각형의 RGB 색상값에서 붉은색과 녹색 값만을 변수 `i`와 `j`를 사용하여 변경합니다. 파란색 채널은 고정된 값입니다. 채널 값들을 변경함으로써, 모든 종류의 팔레트를 생성할 수 있습니다. 단계를 늘리면 Photoshop에서 사용하는 색상 팔레트와 비슷한 모양을 얻을 수 있습니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");
  for (var i = 0; i < 6; i++) {
    for (var j = 0; j < 6; j++) {
      ctx.fillStyle =
        "rgb(" +
        Math.floor(255 - 42.5 \* i) +
        ", " +
        Math.floor(255 - 42.5 \* j) +
        ", 0)";
      ctx.fillRect(j \* 25, i \* 25, 25, 25);
}
```

```
<canvas id="canvas" width="150" height="150"></canvas>
```

```
draw();
```

결과는 아래와 같습니다.

| Screenshot | Live sample |
| --- | --- |
|  |  |

### `strokeStyle` 예제

이번 예제는 위에 나온 예제와 비슷하지만, `strokeStyle` 속성을 사용하여 윤곽선의 색을 바꿉니다. 사각형 대신, 원을 그리는 `arc()` 메소드를 사용합니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");
  for (var i = 0; i < 6; i++) {
    for (var j = 0; j < 6; j++) {
      ctx.strokeStyle =
        "rgb(0, " +
        Math.floor(255 - 42.5 \* i) +
        ", " +
        Math.floor(255 - 42.5 \* j) +
        ")";
      ctx.beginPath();
      ctx.arc(12.5 + j \* 25, 12.5 + i \* 25, 10, 0, Math.PI \* 2, true);
      ctx.stroke();
    }
  }
}
```

```
<canvas id="canvas" width="150" height="150"></canvas>
```

```
draw();
```

결과는 아래와 같습니다.

| Screenshot | Live sample |
| --- | --- |
|  |  |

투명도
---

캔버스에는 불투명한 도형을 그릴 수도 있고, 반투명한 도형도 그릴 수도 있습니다. `globalAlpha` 값을 설정하거나 윤곽선 또는 채움 스타일에 반투명 색을 적용하면 됩니다.

`globalAlpha = transparencyValue` (en-US)

투명도 값이 설정되면 이후 캔버스에 그려지는 모든 도형들의 투명도가 바뀝니다. 설정하는 값은 0.0(완전히 투명)과 1.0(완전히 불투명) 사이에 있어야 하며, 초기 설정값은 1.0(완전히 불투명)입니다.

`globalAlpha`는 모두 같은 투명도로 캔버스에 많은 도형을 그릴 때 유용합니다. 하지만, 보통은 각각의 도형마다 투명도를 설정하는 것이 더 유용할 것입니다.

`strokeStyle`과 `fillStyle` 값에 CSS rgba 색상값을 적용할 수 있으므로, 투명한 색을 적용하려면 아래와 같은 표기법을 사용할 수 있습니다.

```js
// 외곽선과 채움 스타일에 투명 적용

ctx.strokeStyle = "rgba(255, 0, 0, 0.5)";
ctx.fillStyle = "rgba(255, 0, 0, 0.5)";
```

`rgba()` 함수는 `rgb()` 함수와 비슷하지만, 인자가 하나 더 있습니다. 마지막 인자는 투명도 값을 설정하는 인자입니다. 올바른 범위는 0.0(완전히 투명)에서 1.0(완전히 불투명)입니다.

### `globalAlpha` 예제

이 예제에서, 네 가지 다른 색을 가진 사각형을 배경에 그릴 것입니다. 그 위에, 반투명한 원을 여러 개 그릴 것입니다. `globalAlpha` 값을 `0.2`로 설정하면 이후 그려질 도형은 이 값을 사용합니다. `for` 반복문을 사용하여 점점 큰 반지름의 원을 그립니다. 최종 결과물은 원형 그레이디언트가 됩니다. 원이 겹쳐지면서 점점 불투명해지는 것을 볼 수 있으며, 최종적으로 한 가운데에 있는 원에서는 뒷 배경이 거의 보이지 않게 됩니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");
  // 배경을 그린다
  ctx.fillStyle = "#FD0";
  ctx.fillRect(0, 0, 75, 75);
  ctx.fillStyle = "#6C0";
  ctx.fillRect(75, 0, 75, 75);
  ctx.fillStyle = "#09F";
  ctx.fillRect(0, 75, 75, 75);
  ctx.fillStyle = "#F30";
  ctx.fillRect(75, 75, 75, 75);
  ctx.fillStyle = "#FFF";

  // 투명값을 설정한다
  ctx.globalAlpha = 0.2;

  // 반투명한 원을 그린다
  for (var i = 0; i < 7; i++) {
    ctx.beginPath();
    ctx.arc(75, 75, 10 + 10 \* i, 0, Math.PI \* 2, true);
    ctx.fill();
  }
}
```

```
<canvas id="canvas" width="150" height="150"></canvas>
```

```
draw();
```

| Screenshot | Live sample |
| --- | --- |
|  |  |

### `rgba()`를 사용한 예제

두번째 예제에서는 위의 예제와 비슷한 일을 하지만, 겹쳐진 원을 그리는 대신, 불투명도가 증가하는 작은 사각형을 그릴 것입니다. 각각의 도형마다 윤곽선이나 채움 스타일을 따로따로 설정할 수 있기 때문에, `rgba()`를 사용할 때는 조금 더 제어가 쉽고 융통성도 있습니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");

  // 배경을 그린다
  ctx.fillStyle = "rgb(255,221,0)";
  ctx.fillRect(0, 0, 150, 37.5);
  ctx.fillStyle = "rgb(102,204,0)";
  ctx.fillRect(0, 37.5, 150, 37.5);
  ctx.fillStyle = "rgb(0,153,255)";
  ctx.fillRect(0, 75, 150, 37.5);
  ctx.fillStyle = "rgb(255,51,0)";
  ctx.fillRect(0, 112.5, 150, 37.5);

  // 반투명한 사각형을 그린다
  for (var i = 0; i < 10; i++) {
    ctx.fillStyle = "rgba(255,255,255," + (i + 1) / 10 + ")";
    for (var j = 0; j < 4; j++) {
      ctx.fillRect(5 + i \* 14, 5 + j \* 37.5, 14, 27.5);
    }
  }
}
```

```
<canvas id="canvas" width="150" height="150"></canvas>
```

```
draw();
```

| Screenshot | Live sample |
| --- | --- |
|  |  |

선 모양
----

선의 스타일을 바꾸는 방법이 몇가지 있습니다.

`lineWidth = value` (en-US)

이후 그려질 선의 두께를 설정합니다.

`lineCap = type` (en-US)

선의 끝 모양을 설정합니다.

`lineJoin = type` (en-US)

선들이 만나는 "모서리"의 모양을 설정합니다.

`miterLimit = value` (en-US)

두 선이 예각으로 만날 때 접합점의 두께를 제어할 수 있도록, 연결부위의 크기를 제한하는 값을 설정합니다.

`getLineDash()` (en-US)

음수가 아닌 짝수를 포함하는 현재 선의 대시 패턴 배열을 반환합니다.

`setLineDash(segments)` (en-US)

현재 선의 대시 패턴을 설정합니다.

`lineDashOffset = value` (en-US)

선의 대시 배열이 어디서 시작될지 지정합니다.

아래 예제들을 보면 어떻게 동작하는지 이해할 수 있을 것입니다.

### `lineWidth` 예제

현재 선의 두께를 설정합니다. 설정값은 반드시 양수이어야 하며, 초기 설정값은 1.0 단위입니다.

선의 두께는 지정된 경로의 가운데에 있는 획의 두께입니다. 이 말의 뜻은, 경로의 좌우로, 설정된 두께 반 만큼의 폭 영역이 그려진다는 것입니다. 캔버스 좌표는 픽셀을 직접 참조하지 않으므로, 선명한 수평 및 수직선을 얻기 위해 특별히 주의를 기울여야 합니다.

아래에 나오는 예제에서는, 선의 두께가 점점 증가하는 10개의 직선이 그려집니다. 가장 왼쪽의 선은 1.0 단위 폭입니다. 그러나, 경로의 위치 때문에 가장 왼쪽과 다른 모든 홀수 폭 두께 선은 선명하게 보이지 않습니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");
  for (var i = 0; i < 10; i++) {
    ctx.lineWidth = 1 + i;
    ctx.beginPath();
    ctx.moveTo(5 + i \* 14, 5);
    ctx.lineTo(5 + i \* 14, 140);
    ctx.stroke();
  }
}
```

```
<canvas id="canvas" width="150" height="150"></canvas>
```

```
draw();
```

| Screenshot | Live sample |
| --- | --- |
|  |  |

선명한 선을 얻으려면 경로에 획을 어떻게 그려지는지 이해해야 합니다. 아래의 이미지를 보면, 격자는 캔버스의 좌표 격자를 나타냅니다. 격자선 사이에 있는 사각형은 실제 픽셀과 딱 맞아 떨어집니다. 아래에 있는 첫번째 이미지를 보면, (2,1)에서 (5,5)로 사각형이 채워져 있습니다. 사각형의 전체 영역(연한 붉은 색)은 픽셀 경계선 사이에 딱 맞아 떨어지기 때문에 채워진 사각형은 선명한 가장자리를 갖습니다.

![](/ko/docs/Web/API/Canvas_API/Tutorial/Applying_styles_and_colors/canvas-grid.png)

만일 (3,1)에서 (3,5)로 그리는 직선의 두께가 1.0이라면, 두번째 이미지와 같은 상황이 됩니다. 채워진 실제 영역 (진한 파란색)은 패스의 양쪽에있는 픽셀의 절반까지만 확장됩니다. 이것은 1 픽셀을 채우는 것이 아니므로 근사값으로 화면에 그려지게 됩니다. 그래서 양옆의 영역(연한 파란색과 짙은 파란 색)으로, 실제 설정한 색과는 다른 흐릿한 색으로 채워지는 것입니다. 이전 예제에서 보듯이 선 두께가 `1.0`인 선에서 일어난 일입니다.

이렇게 되는 것을 막으려면, 경로를 아주 정밀하게 생성해야 합니다. 선의 두께가 1.0이면 경로의 양쪽으로 0.5 단위만큼이라는 것을 알고 있으니, (3.5,1)에서 (3.5,5)로 그리는 경로를 생성하는 세번째 이미지의 결과는 완벽히 정밀하게 1 픽셀 두께의 수직선이 됩니다.

**참고:** **참고:** 위에 나온 수직선 그리기 예제를 살펴보면, Y 위치는 정수로 된 격자선 위치를 참조하고 있습니다. 그렇지 않았다면, 끝점에서 픽셀의 반을 차지한 상태로 보였을 것입니다. (초기값이 `butt`인 `lineCap` 스타일의 설정값에 따라 다르게 보입니다. 홀수 두께 선들의 좌표를 0.5 픽셀씩 조정하여 다시 계산하고 싶을지도 모릅니다. `lineCap` 스타일을 `square`로 설정함으로써, 끝점에서 선의 외곽 경계선은 픽셀에 딱 맞게 자동적으로 확장될 것입니다.)경로의 시작 지점과 종료 지점의 끝점만이 영향을 받는다는 것에 주목하세요. 만약 `closePath()`로 경로가 닫힌다면, 시작 지점과 종료 지점은 없는 것입니다. 그 대신, 경로 안에 있는 모든 끝점들은, 초기 설정값이 `miter`인 `lineJoin` 스타일의 설정값을 사용하여 이전 부분 및 다음 부분과 이어지는데, 교차되는 점들로 이어진 부분들의 외곽 경계선을 자동적으로 확장하는 효과가 생깁니다. 그렇기 때문에 만약 이들 이어진 부분들이 수직 또는 수평이라면, 그려지는 선들은 각 끝점의 중심에 놓인 픽셀을 가득 채우게 될 것입니다. 이들 선 스타일에 대한 예제는 아래에 나옵니다.

짝수 두께의 선들은 반으로 나누어도 각각의 반은 정수의 양만큼이기 때문에 픽셀을 조정할 필요가 없습니다.

비트맵이 아닌 벡터 2D 그래픽으로 작업할 때, 작업을 시작할 때는 약간 힘들겠지만, 격자와 경로의 위치에 주의를 기울인다면, 크기를 키우거나 줄이거나 또는 어떠한 변형을 하더라도 그리려는 이미지는 똑바로 보일 것입니다. 1.0 두께의 수직선은 2배로 크기를 키웠을 때, 정확히 2 픽셀 두께의 선이 되며, 올바른 위치에 나타날 것입니다.

### `lineCap` 예제

`lineCap` 속성은 그리려는 모든 선의 끝점 모양을 결정합니다. `butt`, `round`, `square`라는 세 가지 값을 가지며, 초기 설정값은 `butt`입니다.

![](/ko/docs/Web/API/Canvas_API/Tutorial/Applying_styles_and_colors/canvas_linecap.png)

`butt`

선의 끝이 좌표에 딱맞게 잘립니다.

`round`

선의 끝이 동그랗습니다.

`square`

선 끝에, 선 두께 반만큼의 사각형 영역이 더해집니다.

이 예제에서는, 각기 다른 `lineCap` 속성 값을 가진 선 세개가 그려집니다. 또한 각 선들의 차이점을 정확히 보이기 위한 안내선이 추가로 그려집니다. 세개의 선 모두, 이 안내선 위에 시작과 종료 좌표가 있습니다.

맨 왼쪽에 있는 선은 초기 설정값인 `butt`을 사용합니다. 안내선에 딱 맞게 선이 시작되고 끝이 납니다. 가운데에 있는 선은 `round`를 사용합니다. 선 두께의 반을 반지름으로 하는 반원이 끝에 붙습니다. 맨 오른쪽에 있는 선은 `square`를 사용합니다. 선 두께 만큼의 너비와 선 두께 반 만큼의 높이를 가진 네모 상자가 끝에 붙습니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");
  var lineCap = ["butt", "round", "square"];

  // 안내선을 그린다
  ctx.strokeStyle = "#09f";
  ctx.beginPath();
  ctx.moveTo(10, 10);
  ctx.lineTo(140, 10);
  ctx.moveTo(10, 140);
  ctx.lineTo(140, 140);
  ctx.stroke();

  // 선을 그린다
  ctx.strokeStyle = "black";
  for (var i = 0; i < lineCap.length; i++) {
    ctx.lineWidth = 15;
    ctx.lineCap = lineCap[i];
    ctx.beginPath();
    ctx.moveTo(25 + i \* 50, 10);
    ctx.lineTo(25 + i \* 50, 140);
    ctx.stroke();
  }
}
```

```
<canvas id="canvas" width="150" height="150"></canvas>
```

```
draw();
```

| Screenshot | Live sample |
| --- | --- |
|  |  |

### `lineJoin` 예제

`lineJoin` 속성은, 도형을 이루는 선이나 호나 곡선들이 연결되는 지점의 모양을 결정합니다. 끝점과 제어점이 정확히 같은 위치인, 길이가 0인 부분들은 제외된다.

이 속성에는 세 가지 값이 있는데, `round`, `bevel`, `miter`이며, 초기 설정값은 `miter`입니다. 두 부분들이 같은 방향으로 연결되어 있는 경우에는, `lineJoin`을 설정해도 아무런 효과가 나타나지 않습니다.

![](/ko/docs/Web/API/Canvas_API/Tutorial/Applying_styles_and_colors/canvas_linejoin.png)

`round`

도형의 모서리를, 연결되는 부분들의 공통 끝점을 중심으로 하는 원 모양으로 만듭니다. 이때 원의 반지름은 선의 두께와 같습니다.

`bevel`

도형의 모서리를, 연결되는 부분들의 공통 끝점에서 세모 모양으로 만듭니다.

`miter`

도형의 모서리를, 두 부분의 바깥쪽 테두리 선을 각각 연장하여 교차된 점으로 생긴 마름모꼴 모양으로 만듭니다. `miterLimit` 속성값에 따라 모양이 달라집니다.

아래 예제에서 세 개의 경로를 그릴 것입니다. 세 경로는 각각 다른 `lineJoin` 속성값을 가집니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");
  var lineJoin = ["round", "bevel", "miter"];
  ctx.lineWidth = 10;
  for (var i = 0; i < lineJoin.length; i++) {
    ctx.lineJoin = lineJoin[i];
    ctx.beginPath();
    ctx.moveTo(-5, 5 + i \* 40);
    ctx.lineTo(35, 45 + i \* 40);
    ctx.lineTo(75, 5 + i \* 40);
    ctx.lineTo(115, 45 + i \* 40);
    ctx.lineTo(155, 5 + i \* 40);
    ctx.stroke();
  }
}
```

```
<canvas id="canvas" width="150" height="150"></canvas>
```

```
draw();
```

| Screenshot | Live sample |
| --- | --- |
|  |  |

### `miterLimit` 속성 예제

위의 예제에서 볼 수 있듯이, 속성값을 `miter`로 하여 두 선이 연결되면, 연결되는 두 선의 바깥쪽 테두리는 연장선이 만나는 지점까지 확장됩니다. 연결된 두 선이 이루는 각도가 크면, 확장되는 영역이 크지 않지만, 각도가 작아짐(끝이 뾰족해짐)에 따라서 이 영역이 기하급수적으로 커질 수도 있습니다.

`miterLimit` 속성은 끝점이 만나는 지점과 테두리 연장선이 만나는 지점이 얼마나 떨어져 있을지를 결정합니다. 두 선이 이 값을 넘게 되면, `lineJoin` 속성의 `bevel` 값 모양이 적용됩니다. `miterLimit` 속성값(HTML `<canvas>`에서, 초기 설정값은 10.0)에 의해, 현재 좌표 방식 안에서, 선의 두께에 따라, 어느 정도까지 뾰족해질 수 있는지가 계산됩니다. 그래서 `miterLimit`은 현재 디스플레이 비율이나 경로의 변형 같은 것으로 각각 설정될 수 있습니다. 그렇게 하여 선의 모서리에만 영향을 줍니다.

더 자세히 얘기하자면, 뾰족함 제한(miter limit)은, 선 두께의 반과 확장되는 길이(HTML 캔버스에서, 선이 연결되는 바깥쪽 끝부분과, 경로에서 연결되는 두 부분의 공통 끝점 사이로 측정합니다.)의 최대 허용 비율입니다. 이것은 두 부분의 외곽선이 만나는 안쪽 점과 바깥쪽 점 사이 거리와, 선 두께의 최대 허용 비율과 같습니다. miter 값 모양이 아닌 bevel 값 모양으로 연결되는 지점의 최소 안쪽 각 반 만큼의 값과 같은 것입니다.

* `miterLimit` = **max** `miterLength` / `lineWidth` = 1 / **sin** ( **min** *θ* / 2 )
* 초기 설정값이 10.0인 뾰족함 제한(miter limit)값은 약 11도보다 낮은 예각인 곳을 bevel 값 모양으로 만듭니다.
* 뾰족함 제한 값이 √2 ≈ 1.4142136(반올림)과 같다면 연결되는 곳이 둔각이거나 직각인 곳을 제외한 모든 곳을 bevel 값 모양으로 만듭니다.
* 뾰족함 제한 값이 1.0과 같다면 모든 곳을 bevel 값 모양으로 만듭니다.
* 뾰족함 제한 값에는 1.0보다 작은 값이 올 수 없습니다.

다음 예제에서는 `miterLimit` 값을 바꾸고 그 결과가 어떤지 바로 확인할 수 있습니다. 파란색 선은 지그재그 무늬 안에서 선들의 시작 지점과 종료 지점을 나타냅니다.

이 예제에서 `miterLimit` 값을 4.2보다 낮게 설정하면, 모든 연결 지점은 bevel 값 모양이 되어 파란색 선에 붙을 것입니다. `miterLimit` 값이 10보다 높다면, 거의 모든 연결 지점들이 파란색 선 바깥쪽에 있을 것입니다. 왼쪽으로 갈수록 파란색 선에서 더 많이 튀어나오는데, 왼쪽으로 갈수록 연결되는 각이 더 작아지기 때문입니다. 값을 5로 설정하면, 반은 bevel 값 모양으로, 반은 miter 값 모양이 됩니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");

  // 캔버스를 비운다
  ctx.clearRect(0, 0, 150, 150);

  // 안내선을 그린다
  ctx.strokeStyle = "#09f";
  ctx.lineWidth = 2;
  ctx.strokeRect(-5, 50, 160, 50);

  // 선 스타일을 설정한다
  ctx.strokeStyle = "#000";
  ctx.lineWidth = 10;

  // 입력 값을 검사한다
  if (document.getElementById("miterLimit").value.match(/\d+(\.\d+)?/)) {
    ctx.miterLimit = parseFloat(document.getElementById("miterLimit").value);
  } else {
    alert("Value must be a positive number");
  }

  // 선을 그린다
  ctx.beginPath();
  ctx.moveTo(0, 100);
  for (i = 0; i < 24; i++) {
    var dy = i % 2 == 0 ? 25 : -25;
    ctx.lineTo(Math.pow(i, 1.5) \* 2, 75 + dy);
  }
  ctx.stroke();
  return false;
}
```

```
<table>
  <tr>
    <td><canvas id="canvas" width="150" height="150"></canvas></td>
    <td>
      Change the <code>miterLimit</code> by entering a new value below and
      clicking the redraw button.<br /><br />
      <form onsubmit="return draw();">
        <label>Miter limit</label>
        <input type="text" size="3" id="miterLimit" />
        <input type="submit" value="Redraw" />
      </form>
    </td>
  </tr>
</table>
```

```
document.getElementById("miterLimit").value = document
  .getElementById("canvas")
  .getContext("2d").miterLimit;
draw();
```

| Screenshot | Live sample |
| --- | --- |
|  |  |

### 대시라인 사용하기

`setLineDash` 메소드와 `lineDashOffset` 속성은 선의 대시 패턴을 지정합니다. `setLineDash` 메소드는 거리를 지정하는 숫자 목록을 받아 선과 틈을 교대로 그리며 `lineDashOffset` 속성은 패턴을 시작할 위치를 오프셋으로 설정합니다.

이 예제에서 우리는 행진하는 개미 효과를 만들고 있습니다. 컴퓨터 그래픽 프로그램의 선택 도구에서 흔히 볼 수있는 애니메이션 기술입니다. 테두리를 애니메이션화하여 사용자가 선택 테두리와 이미지 배경을 구별하는 데 도움이됩니다. 이 튜토리얼의 뒷부분에서 이 작업 및 다른 기본 애니메이션을 수행하는 방법을 배울 수 있습니다.
```

<canvas id="canvas" width="110" height="110"></canvas>
```

```js
var ctx = document.getElementById("canvas").getContext("2d");
var offset = 0;

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.setLineDash([4, 2]);
  ctx.lineDashOffset = -offset;
  ctx.strokeRect(10, 10, 100, 100);
}

function march() {
  offset++;
  if (offset > 16) {
    offset = 0;
  }
  draw();
  setTimeout(march, 20);
}

march();
```

| Screenshot | Live sample |
| --- | --- |
|  |  |

그라디언트(Gradient)
---------------

다른 그래픽 프로그램들과 마찬가지로, 선형 및 원형의 그레이디언트를 사용할 수 있습니다. 다음 메소드 중 하나를 사용하여 `CanvasGradient` (en-US) 객체를 생성합니다. 그런 다음 이 객체를 `fillStyle` 또는 `strokeStyle` 속성에 할당 할 수 있습니다.

`createLinearGradient(x1, y1, x2, y2)` (en-US)

시작점 좌표를 (`x1`, `y1`)로 하고, 종료점 좌표를 (`x2`, `y2`)로 하는 선형 그라디언트 오브젝트를 생성합니다.

`createRadialGradient(x1, y1, r1, x2, y2, r2)` (en-US)

반지름이 `r1`이고 좌표 (`x1`, `y1`)을 중심으로 하는 원과, 반지름이 `r2`이고 좌표 (`x2`, `y2`)를 중심으로 하는 원을 사용하여 그라디언트가 생성됩니다.

예를 들면 다음과 같습니다.

```js
var lineargradient = ctx.createLinearGradient(0, 0, 150, 150);
var radialgradient = ctx.createRadialGradient(75, 75, 0, 75, 75, 100);
```

`CanvasGradient` 객체를 만들었다면, `addColorStop()` 메소드를 사용하여, 오브젝트에 색을 적용할 수 있습니다.

`gradient.addColorStop(position, color)` (en-US)

`gradient` 오브젝트에 새로운 색 중단점(color stop)을 생성합니다. `position`은 0.0에서 1.0 사이의 숫자이고 그라디언트에서 색상의 상대적인 위치를 정의합니다. `color` 인자는 CSS `<color>`를 나타내는 문자열이어야하고, 그라디언트가 (전환효과로) 진행되면서 도달한 지점의 색상을 의미합니다.

색 중단점은 원하는 만큼 마음대로 추가할 수 있습니다. 흰 색에서 검은 색으로 변하는 선형 그레이디언트를 만들려면 아래와 같이 합니다.

```js
var lineargradient = ctx.createLinearGradient(0, 0, 150, 150);
lineargradient.addColorStop(0, "white");
lineargradient.addColorStop(1, "black");
```

### `createLinearGradient` 예제

이 예제에서 그레이디언트 두 개를 만들 것입니다. 예제에서 볼 수 있듯이, `strokeStyle`과 `fillStyle` 속성 모두 `canvasGradient` 오브젝트를 속성 값으로 가질 수 있습니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");

  // 그레이디언트를 생성한다
  var lingrad = ctx.createLinearGradient(0, 0, 0, 150);
  lingrad.addColorStop(0, "#00ABEB");
  lingrad.addColorStop(0.5, "#fff");
  lingrad.addColorStop(0.5, "#26C000");
  lingrad.addColorStop(1, "#fff");

  var lingrad2 = ctx.createLinearGradient(0, 50, 0, 95);
  lingrad2.addColorStop(0.5, "#000");
  lingrad2.addColorStop(1, "rgba(0, 0, 0, 0)");

  // 외곽선과 채움 스타일에 그레이디언트를 적용한다
  ctx.fillStyle = lingrad;
  ctx.strokeStyle = lingrad2;

  // 도형을 그린다
  ctx.fillRect(10, 10, 130, 130);
  ctx.strokeRect(50, 50, 50, 50);
}
```

```
<canvas id="canvas" width="150" height="150"></canvas>
```

```
draw();
```

첫번째 그라디언트는 배경 그라디언트입니다. 보시다시피 같은 위치에 두 가지 색상을 지정했습니다. 매우 선명한 색상 전환을 만들기 위해 이 작업을 수행합니다(이 경우 흰색에서 녹색으로). 일반적으로 색상 중단점을 정의하는 순서는 중요하지 않지만, 이 특별한 경우에는 의미가 있습니다.

두 번째 그래디언트에서는 시작 색상 (위치 0.0)을 지정하지 않았는데, 자동으로 다음 색상 중단점의 색상으로 가정하기 때문에 반드시 필요하지는 않기 때문입니다. 따라서 위치 0.5에 검은색을 지정하면 시작부터 중단점까지 자동으로 검정색 그라이언트를 만듭니다.

| Screenshot | Live sample |
| --- | --- |
|  |  |

### `createRadialGradient` 예제

이 예제에서는 원형 그레이디언트를 4개 만들 것입니다. 포토샵같은 프로그램에서 원형 그레이디언트를 만들 때는 하나의 점을 중심으로 그레이디언트를 만드는데, 캔버스의 원형 그레이디언트에서는 시작과 종료 지점 두군데를 제어할 수 있기 때문에, 기존의 프로그램에서 볼 수 있는 원형 그레이디언트보다는 더 복잡한 효과를 만들어 낼 수 있습니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");

  // 그라디언트를 생성한다
  var radgrad = ctx.createRadialGradient(45, 45, 10, 52, 50, 30);
  radgrad.addColorStop(0, "#A7D30C");
  radgrad.addColorStop(0.9, "#019F62");
  radgrad.addColorStop(1, "rgba(1,159,98,0)");

  var radgrad2 = ctx.createRadialGradient(105, 105, 20, 112, 120, 50);
  radgrad2.addColorStop(0, "#FF5F98");
  radgrad2.addColorStop(0.75, "#FF0188");
  radgrad2.addColorStop(1, "rgba(255,1,136,0)");

  var radgrad3 = ctx.createRadialGradient(95, 15, 15, 102, 20, 40);
  radgrad3.addColorStop(0, "#00C9FF");
  radgrad3.addColorStop(0.8, "#00B5E2");
  radgrad3.addColorStop(1, "rgba(0,201,255,0)");

  var radgrad4 = ctx.createRadialGradient(0, 150, 50, 0, 140, 90);
  radgrad4.addColorStop(0, "#F4F201");
  radgrad4.addColorStop(0.8, "#E4C700");
  radgrad4.addColorStop(1, "rgba(228,199,0,0)");

  // 도형을 그린다
  ctx.fillStyle = radgrad4;
  ctx.fillRect(0, 0, 150, 150);
  ctx.fillStyle = radgrad3;
  ctx.fillRect(0, 0, 150, 150);
  ctx.fillStyle = radgrad2;
  ctx.fillRect(0, 0, 150, 150);
  ctx.fillStyle = radgrad;
  ctx.fillRect(0, 0, 150, 150);
}
```

```
<canvas id="canvas" width="150" height="150"></canvas>
```

```
draw();
```

이 예제에서는 원형 그레이디언트에 사용되는 두 원의 중심을 달리하여 입체적인 공처럼 보이게 했습니다. 안쪽 원과 바깥쪽 원은 겹치지 않게 하는 것이 좋습니다. 왜냐하면 예상하기 힘든 이상한 결과가 나타날 수 있기 때문입니다.

그레이디언트의 마지막 색 적용 지점에서는 투명도를 적용하였습니다. 투명도가 적용된 지점에서 이전 지점까지의 색 변화를 보기 좋게 만들려면, 두 지점에 똑같은 색을 적용하면 되는데, 이 예제에서는 색의 값을 다른 방식으로 입력하여 헷갈릴 수도 있습니다. 첫번째 그레이디언트에 사용된 `#019F62`와 `rgba(1,159,98,1)`은 같은 색입니다.

| Screenshot | Live sample |
| --- | --- |
|  |  |

### `createConicGradient` 예제

이 예제에서는 서로 다른 두개의 원뿔 (conic) 그레이디언트를 정의할 것입니다. 원뿔 그레이디언트는 원을 만드는 원형 그레이디언트와 다르게 점을 기준으로 원을 그립니다.

```js
function draw() {
  const ctx = document.getElementById("canvas").getContext("2d");

  // 그레이디언트를 생성한다
  const conicGrad1 = ctx.createConicGradient(2, 62, 75);
  conicGrad1.addColorStop(0, "#A7D30C");
  conicGrad1.addColorStop(1, "#fff");

  const conicGrad2 = ctx.createConicGradient(0, 187, 75);
  // 도(degree)에서 라디안으로 변환하기 위해 값에 Math.PI/180 를 곱한다
  conicGrad2.addColorStop(0, "black");
  conicGrad2.addColorStop(0.25, "black");
  conicGrad2.addColorStop(0.25, "white");
  conicGrad2.addColorStop(0.5, "white");
  conicGrad2.addColorStop(0.5, "black");
  conicGrad2.addColorStop(0.75, "black");
  conicGrad2.addColorStop(0.75, "white");
  conicGrad2.addColorStop(1, "white");

  // 도형을 그린다
  ctx.fillStyle = conicGrad1;
  ctx.fillRect(12, 25, 100, 100);
  ctx.fillStyle = conicGrad2;
  ctx.fillRect(137, 25, 100, 100);
}
```

```
<canvas id="canvas" width="250" height="150" role="presentation">
  원뿔 그레이디언트
</canvas>
```

```
draw();
```

첫 번째 그레이디언트는 첫 번째 직사각형의 중앙에 위치하고 시작할 때 녹색 정지점을 끝에서 흰색 정지점으로 이동합니다. 각도는 2 라디안에서 시작하는데, 이는 남동쪽을 가리키는 시작 / 끝선 이기에 눈에 띕니다.

두 번쨰 그레이디언트는 두 번째 사각형의 중앙에 위치합니다. 이 그레이디언트는 회전할 때마다 검은색에서 흰색으로 번갈아 가며 여러 가지 색상을 표현합니다. 이는 체크 효과를 나타나게 됩니다.

| Screenshot | Live sample |
| --- | --- |
|  |  |

패턴(Patterns)
------------

이전 페이지의 예제 중 하나에서 일련의 루프를 사용하여 이미지 패턴을 만들었습니다. 그러나 훨씬 간단한 메소드 인 createPattern () 메소드가 있습니다.

`createPattern(image, type)` (en-US)

새 캔버스 패턴 객체를 만들어 반환합니다. `image`는 `CanvasImageSource` (en-US)(즉, `HTMLImageElement` (en-US), 다른 캔버스, `<video>` 요소 등등)입니다. `type`은 이미지 사용 방법을 나타내는 문자열입니다.

`type`은 패턴을 만들기 위해 이미지를 사용하는 방법을 지정하며, 다음 문자열 값 중 하나 여야합니다.

`repeat`

수직 및 수평 방향으로 이미지를 이어 붙입니다.

`repeat-x`

수평 방향으로만 이미지를 이어 붙입니다.

`repeat-y`

수직 방향으로만 이미지를 이어 붙입니다.

`no-repeat`

이미지를 이어 붙이지 않습니다. 이미지는 한번만 사용됩니다.

이 메소드를 사용하여 위에서 본 그라디언트 메소드와 매우 유사한 `CanvasPattern` (en-US) 객체를 생성합니다. 패턴을 생성하면 `fillStyle` 또는 `strokeStyle` 속성에 패턴을 할당 할 수 있습니다. 예를 들면 다음과 같습니다.

```js
var img = new Image();
img.src = "someimage.png";
var ptrn = ctx.createPattern(img, "repeat");
```

**참고:** `drawImage ()` 메서드와 마찬가지로 이 메소드를 호출하기 전에 이미지가 로드되었는지 확인해야합니다. 그렇지 않으면 패턴이 잘못 그려 질 수 있습니다.

### `createPattern` 예제

이 마지막 예제에서, `fillStyle` 속성에 적용할 패턴을 만들 것입니다. 한 가지 눈여겨 볼 것은, 이미지 `onload` 핸들러 사용한다는 것입니다. 이미지를 패턴에 적용하기 전에 불러오기가 완료되었는지 확인하는 것입니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");

  // 패턴으로 사용할 이미지 오브젝트를 생성한다
  var img = new Image();
  img.src = "canvas\_createpattern.png";
  img.onload = function () {
    // 패턴을 생성한다
    var ptrn = ctx.createPattern(img, "repeat");
    ctx.fillStyle = ptrn;
    ctx.fillRect(0, 0, 150, 150);
  };
}
```

```
<canvas id="canvas" width="150" height="150"></canvas>
```

```
draw();
```

The result looks like this:

| Screenshot | Live sample |
| --- | --- |
|  |  |

그림자
---

그림자 사용에는 네 개의 속성이 있습니다.

`shadowOffsetX = float` (en-US)

그림자가 객체에서 연장되어야하는 수평 거리를 나타냅니다. 이 값은 변환 행렬의 영향을 받지 않습니다. 기본값은 0입니다.

`shadowOffsetY = float` (en-US)

그림자가 객체에서 연장되어야하는 수직 거리를 나타냅니다. 이 값은 변환 행렬의 영향을 받지 않습니다. 기본값은 0입니다.

`shadowBlur = float` (en-US)

흐림(blur) 효과의 크기를 나타냅니다. 이 값은 픽셀 수와 일치하지 않으며 현재 변환 행렬의 영향을 받지 않습니다. 기본값은 0입니다.

`shadowColor = color` (en-US)

그림자 효과의 색상을 나타내는 표준 CSS 색상 값. 기본적으로 완전 검은색입니다.

`shadowOffsetX` 및 `shadowOffsetY` 속성은 그림자가 X 및 Y 방향으로 객체에서 얼마나 멀리 떨어져야하는지 나타냅니다. 이 값은 현재 변환 행렬의 영향을받지 않습니다. 음수값을 사용하면 그림자가 위로 또는 왼쪽으로 확장되고 양수값을 사용하면 그림자가 아래로 또는 오른쪽으로 확장됩니다. 기본값은 모두 0입니다.

`shadowBlur` 속성은 흐림 효과의 크기를 나타냅니다. 이 값은 픽셀 수와 일치하지 않으며 현재 변환 행렬의 영향을받지 않습니다. 기본값은 0입니다.

`shadowColor` 속성은 그림자 효과의 색상을 나타내는 표준 CSS 색상 값입니다. 기본값은 완전 검은색입니다.

**참고:** **알아둘 것:** 음영은 `source-over` 합성 작업에만 사용됩니다.

### 그림자 있는 글자 예제

이 예제에서는 그림자가 있는 글자를 그립니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");

  ctx.shadowOffsetX = 2;
  ctx.shadowOffsetY = 2;
  ctx.shadowBlur = 2;
  ctx.shadowColor = "rgba(0, 0, 0, 0.5)";

  ctx.font = "20px Times New Roman";
  ctx.fillStyle = "Black";
  ctx.fillText("Sample String", 5, 30);
}
```

```
<canvas id="canvas" width="150" height="80"></canvas>
```

```
draw();
```

| Screenshot | Live sample |
| --- | --- |
|  |  |

다음 장에서 텍스트 그리기에 대한 `font` 속성과 `fillText` 메소드를 살펴 보겠습니다.

캔버스 채우기 규칙
----------

`fill` (혹은 `clip` (en-US) 및 `isPointinPath` (en-US))을 사용할 때 한 점이 경로 안쪽 또는 바깥에 있는지 그리고 따라서 채워지는지 여부를 결정하기 위한 채우기 규칙 알고리즘을 선택적으로 제공 할 수 있습니다. 경로가 교차하거나 중첩 된 경우에 유용합니다.

다음 두가지 값을 사용할 수 있습니다:

`nonzero`

non-zero winding rule 알고리즘. 기본값.

`evenodd`

even-odd winding rule 알고리즘.

이 예제에서는 'evenodd' 규칙을 사용합니다.

```js
function draw() {
  var ctx = document.getElementById("canvas").getContext("2d");
  ctx.beginPath();
  ctx.arc(50, 50, 30, 0, Math.PI \* 2, true);
  ctx.arc(50, 50, 15, 0, Math.PI \* 2, true);
  ctx.fill("evenodd");
}
```

```
<canvas id="canvas" width="100" height="100"></canvas>
```

```
draw();
```

| Screenshot | Live sample |
| --- | --- |
|  |  |

* « 이전
* 다음 »


DOM 소개 - Web API

DOM 소개
======

이 문서는 DOM에 대한 개념을 간략하게 소개하는 문서이다: DOM 이 무엇이며, 그것이 어떻게 HTML, XML 문서들을 위한 구조를 제공하는지, 어떻게 DOM 에 접근하는지, API 가 어떻게 사용되는지에 대한 참조 정보와 예제들을 제공한다.

DOM 이란?
-------

문서 객체 모델(The Document Object Model, 이하 DOM) 은 HTML, XML 문서의 프로그래밍 interface 이다. DOM은 문서의 구조화된 표현(structured representation)을 제공하며 프로그래밍 언어가 DOM 구조에 접근할 수 있는 방법을 제공하여 그들이 문서 구조, 스타일, 내용 등을 변경할 수 있게 돕는다. DOM 은 nodes와 objects로 문서를 표현한다. 이들은 웹 페이지를 스크립트 또는 프로그래밍 언어들에서 사용될 수 있게 연결시켜주는 역할을 담당한다.

웹 페이지는 일종의 문서(document)다. 이 문서는 웹 브라우저를 통해 그 내용이 해석되어 웹 브라우저 화면에 나타나거나 HTML 소스 자체로 나타나기도 한다. 동일한 문서를 사용하여 이처럼 다른 형태로 나타날 수 있다는 점에 주목할 필요가 있다. DOM 은 동일한 문서를 표현하고, 저장하고, 조작하는 방법을 제공한다. DOM 은 웹 페이지의 객체 지향 표현이며, JavaScript와 같은 스크립팅 언어를 이용해 DOM 을 수정할 수 있다.

W3C DOM, WHATWG DOM 표준은 대부분의 브라우저에서 DOM 을 구현하는 기준이다. 많은 브라우저들이 표준 규약에서 제공하는 기능 외에도 추가적인 기능들을 제공하기 때문에 사용자가 작성한 문서들이 각기 다른 DOM 이 적용된 다양한 브라우저 환경에서 동작할 수 있다는 사실을 항상 인지하고 있어야 한다.

예를 들어, 표준 DOM 에서는 문서 안에서 모든 `<P>` elements 에 대한 list 를 리턴하는 `getElementsByTagName` method 를 정의하고 있다:

```js
var paragraphs = document.getElementsByTagName("P");
// paragraphs[0] is the first <p> element
// paragraphs[1] is the second <p> element, etc.
alert(paragraphs[0].nodeName);
```

웹 페이지를 수정하거나 생성하는데 사용되는 모든 property, method, event 들은 objects 로 구성된다. 예를 들어 document object 는 document 자체를 의미하며, table object 는 HTML table 에 접근하기 위한 `HTMLTableElement` DOM 인터페이스를 구현한 것이다. 이 문서는 Gecko 기반의 브라우저에서 구현된 DOM 에 대한 object-by-object reference 를 제공한다.

DOM 과 JavaScript
----------------

이 문서의 대부분의 예제와 같이, 위에서 사용된 예제는 JavaScript이다. 위의 예제는 JavaScript로 작성되었지만 문서(document) 와 문서의 요소(element) 에 접근하기 위해 DOM 이 사용되었다. DOM 은 프로그래밍 언어는 아니지만 DOM 이 없다면 JavaScript 언어는 웹 페이지 또는 XML 페이지 및 요소들과 관련된 모델이나 개념들에 대한 정보를 갖지 못하게 된다. 문서의 모든 element - 전체 문서, 헤드, 문서 안의 table, table header, table cell 안의 text - 는 문서를 위한 document object model 의 한 부분이다. 때문에, 이러한 요소들을 DOM 과 JavaScript와 같은 스크립팅 언어를 통해 접근하고 조작할 수 있는 것이다.

초창기에는 JavaScript와 DOM 가 밀접하게 연결되어 있었지만, 나중에는 각각 분리되어 발전해왔다. 페이지 콘텐츠(the page content)는 DOM 에 저장되고 JavaScript를 통해 접근하거나 조작할 수 있다. 이것을 방정식으로 표현하면 아래와 같다:

API (web or XML page) = DOM + JS (scripting language)

DOM 은 프로그래밍 언어와 독립적으로 디자인되었다. 때문에 문서의 구조적인 표현은 단일 API 를 통해 이용가능하다. 이 문서에서는 JavaScript를 주로 사용하였지만, DOM 의 구현은 어떠한 언어에서도 가능하다. 아래는 파이썬을 사용한 예제이다:

```python
# Python DOM example
import xml.dom.minidom as m
doc = m.parse("C:\\Projects\\Py\\chap1.xml");
doc.nodeName # DOM property of document object;
p_list = doc.getElementsByTagName("para");
```

웹에서 JavaScript 사용하기와 관련된 기술에 대한 추가정보는 JavaScript 기술 개요 문서를 참조하라.

DOM 에 어떻게 접근할 수 있는가?
--------------------

DOM 을 사용하기 위해 특별히 해야할 일은 없다. 각각의 브라우저는 자신만의 방법으로 DOM 구현하였으며, 이로 인해 실제 DOM 기준을 따르는지 확인해야 하는 번거로움이 발생하였다. (이 문제는 이 문서에서 피하고 싶어하는 주제이기도 하다.) 모든 웹 브라우저는 스크립트가 접근할 수 있는 웹 페이지를 만들기 위해 어느 정도의 DOM 을 항상 사용한다.

스크립트를 작성할 때(인라인 `<script>` 요소를 사용하거나 웹 페이지 안에 있는 스크립트 로딩 명령을 사용하여), 문서 자체를 조작하거나 문서의 children 을 얻기 위해 `document` 또는 `window` elements 를 위한 API 를 즉시 사용할 수 있다. DOM 프로그래밍은 아래처럼 `window` object 로 부터 `alert()` (en-US) 함수를 사용하여 alert message 를 표시하는 매우 간단한 것일 수도 있고 다음번 예제처럼 새로운 content 를 작성하는 복잡한 DOM 이 될 수도 있다.

```html
<body onload="window.alert('welcome to my home page!');"></body>
```

아래의 JavaScript는 문서가 로드될 때(모든 DOM을 사용할 수 있게 되는 때임) 실행되는 함수를 정의하였다. 이 함수는 새로운 H1 element 를 생성하고, element 에 text 를 추가하며, H1 을 이 문서의 트리에 추가한다.

```html
<html>
  <head>
    <script>
 // run this function when the document is loaded
 window.onload = function () {
 // create a couple of elements in an otherwise empty HTML page
 var heading = document.createElement("h1");
 var heading\_text = document.createTextNode("Big Head!");
 heading.appendChild(heading\_text);
 document.body.appendChild(heading);
 };
 </script>
  </head>
  <body></body>
</html>
```

중요한 데이터 타입들
-----------

이 문서는 objects 와 types 을 최대한 간단하게 설명하려 한다. API 에는 우리가 반드시 알고 있어야 할 수많은 data types 이 있다는 사실을 염두해 두기 바란다. 이 문서에서는 nodes 는 `element`s 로, 노드의 arrays 는 `nodeList`s(또는 `element`s), attribute 노드들은 `attribute`s 로 표현하였다.

아래의 표는 이러한 data types 에 대한 간략한 설명이다.

|  |  |
| --- | --- |
| `document` |
 member 가 document type 의 object 를 리턴할 때(예를 들어 element의
 **`ownerDocument`** property 는 그것이 속해
 있는 document 를 return 한다. ), 이 object 는 root document object
 자체이다. 는 `document` object 에 대한 설명은
 DOM `document` Reference
 챕터를 참조하라.
  |
| `element` |
`element` 는 DOM API 의 member 에 의해 return 된 element
 또는 `element` type 의 node 를 의미한다.
 document.createElement()
 method 가 `node` 를 참조하는 object 를 리턴한다고 말하는
 대신, 이 method 가 DOM 안에서 생생되는 `element` 를
 리턴한다고 좀 더 단순하게 말할 수 있다. `element` 객체들은
 DOM `Element` interface 와 함께 좀 더 기본적인
 `Node` interface 를 구현한 것이기 때문에 이 reference 에는
 두 가지가 모두 포함되었다고 생각하면 된다.
  |
| `nodeList` |
`nodeList` 는 elements 의 배열이다. (document.getElementsByTagName()
 method 에 의해 리턴된 것과 같은) nodeList의 Items 은 index 를 통해
 접근 가능하며, 다음과 같이 두 가지 방식이 있다:
 * list.item(1)
* list[1]

위의 방식들은 동일한 것이다. **`item()`**method는 `nodeList` object 의 단일 method 이다. 두번째
 방식은 list 에서 두번째 item 을 fetch 하는 전형적인 array syntax 이다.
  |
| `attribute` |
 attribute 가 member 에 의해 리턴되는 것은(예를 들어
 **`createAttribute()`** method 호출에 의한
 리턴), attribute 에 대한 특별한 인터페이스를 노출하는 object reference
 이다. attributes 는 DOM 에서 elements 와 같은 nodes 이다. elements
 만큼 많이 사용되지는 않는다.
  |
| `namedNodeMap` |
`namedNodeMap` 는 array 와 유사하지만 items 은 name 또는
 index 에 의해 접근 가능하다. 리스트는 특별한 정렬이 적용되지 않았기
 enumeration 할 때 index 를 주로 사용한다. `namedNodeMap` 는
 이를 위해 item() method 가 있으며, `namedNodeMap` 에 item
 을 추가하거나 삭제할 수 있다.
  |

DOM interfaces
--------------

이 문서는 objects 와 DOM 에서 조작가능한 것들에 대해 설명하고 있다. 사람들은 HTML FORM element 가 `HTMLFormElement` interface 로부터 **`name`** property 를 얻고, **`className`** property 는 `HTMLElement` interface 로부터 얻는 것에 대해 별로 관심을 보이지 않는 것 같다. 두가지 경우 모두, property 는 form object 안에 있는 것이다.

하지만 DOM 안에 구현된 objects 와 interfaces 사이의 관계는 혼동될 수 있다. 이 섹션에서는 DOM specification 안의 실제 interfaces 와 그들을 어떻게 활용할 수 있는지에 대해 살펴보도록 하겠다.

### Interfaces 와 Objects

많은 objects 가 여러 개의 다른 interfaces 와 연관되어 있다. 예를 들어, table object 는 `createCaption`, `insertRow` method 들이 포함된 `HTMLTableElement` 을 구현한 것이다. table object 는 HTML element 이기도 하기 때문에, `table` 은 `Element` interface(DOM `Element` (en-US) Reference 챕터 참조)도 구현한다. 마지막으로, HTML element 는 DOM 이 연관되어 있는 한 nodes 트리(tree)에서 하나의 node 이다. nodes 트리는 웹 페이지 또는 XML 페이지를 위한 object model 을 구성한다. 때문에 table element 는 보다 기본적인 `Element` 에서 파생된 `Node` interface 를 구현하고 있다.

아래의 예제처럼, `table` object 를 참조하게 되면, 기본적으로 이들 3 가지 interfaces 를 사용할 수 있게 된다.

```js
var table = document.getElementById("table");
var tableAttrs = table.attributes; // Node/Element interface
for (var i = 0; i < tableAttrs.length; i++) {
  // HTMLTableElement interface: border attribute
  if (tableAttrs[i].nodeName.toLowerCase() == "border") table.border = "1";
}
// HTMLTableElement interface: summary attribute
table.summary = "note: increased border";
```

### DOM 의 핵심 Interfaces

이 섹션은 DOM 에서 가장 많이 사용되는 interfaces 를 정리해보았다. 여기에서는 이들 API 가 실제로 어떤 일을 하는지 설명하는 대신 DOM 을 사용하면서 자주 만나게 되는 methods 와 properties 를 보여줄 것이다. 이들 API 는 이 책의 마지막에 소개된 DOM 예제에서도 사용되었다.

`Document` 와 `window` objects 는 DOM 프로그래밍에서 가장 자주 사용하는 objects 이다. 간단하게 설명하자면, `window` object 는 브라우저와 같다고 할 수 있으며, `document` object 는 root document 자체라고 할 수 있다. generic `Node` interface 로부터 상속받은 `Element` 와 `Node`, `Element` interfaces 가 협력하여 각각의 elements 에서 사용할 수 있는 수많은 methods 와 properties 를 제공한다. 이러한 elements 는 이전 섹션에서 설명한 `table` object 예제에서도 살펴봤듯이, elements 가 보유한 데이터를 처리할 수 있는 특정한 interfaces 도 가지고 있다.

아래는 웹 페이지, XML 페이지 스크립팅에서 DOM 을 사용하는 공통적인 API 들의 간략한 목록이다.

* `document.getElementById(id)`
* `document.getElementsByTagName(name)`
* `document.createElement(name)`
* `parentNode.appendChild(node)`
* `element.innerHTML`
* `element.style.left`
* `element.setAttribute`
* `element.getAttribute`
* `element.addEventListener`
* `window.content` (en-US)
* `window.onload` (en-US)
* `window.dump` (en-US)
* `window.scrollTo` (en-US)
DOM API 테스팅
-----------

이 문서는 사용자가 웹 개발에 사용할 수 있는 모든 interface 에 대한 예제를 제공한다. 예제는 `<script>` element 안에서 DOM 에 접근하는 완벽한 HTML 페이지 형태인 것도 있고, form 에서 script 를 실행하기 위해 버튼과 같은 interface 가 필요한 경우도 있으며, DOM 이 목록화되어 수행되는 HTML elements 도 있을 것이다. 사용자들은 이러한 예제를 새로운 HTML 문서에 복사하여 브라우저에서 실행할 수 있다.

어떤 예제는 매우 간단할 수도 있다. HTML elements 에 대한 interface의 기본적인 관계만 보여주는 이러한 예제를 실행할 때는, 스크립트에서 쉽게 접근할 수 있는 test page 를 설정할 수도 있다. 아래의 예제는 interface를 테스트 할 수 있는 함수가 위치할 수 있는 header 안에 `<script>` element 제공한다. 이 함수는 retrieve, set, 조작할 수 있는 attributes 가 포함된 HTML elements 가 사용되었으며, 브라우저에서 이들 함수를 호출하기 위해 웹 UI 를 제공한다.

사용자는 자신이 관심있어 하는 DOM interfaces 를 테스트 하기 위해, 이 test page 를 사용하거나 이와 비슷한 것을 만들어 브라우저에서 어떻게 동작하는지 확인할 수 있다. 사용자는 `test()` 함수 내용을 필요에 따라 업데이트할 수 있다. (버튼 추가, elements 추가 등)

```html
<html>
  <head>
    <title>DOM Tests</title>
    <script type="application/javascript">
 function setBodyAttr(attr, value) {
 if (document.body) eval("document.body." + attr + '="' + value + '"');
 else notSupported();
 }
 </script>
  </head>
  <body>
    <div style="margin: .5in; height: 400;">
      <p>
        <b><tt>text</tt>color</b>
      </p>
      <form>
        <select
 onChange="setBodyAttr('text',
 this.options[this.selectedIndex].value);">
          <option value="black">black</option>
          <option value="darkblue">darkblue</option>
        </select>
        <p>
          <b><tt>bgColor</tt></b>
        </p>
        <select
 onChange="setBodyAttr('bgColor',
 this.options[this.selectedIndex].value);">
          <option value="white">white</option>
          <option value="lightgrey">gray</option>
        </select>
        <p>
          <b><tt>link</tt></b>
        </p>
        <select
 onChange="setBodyAttr('link',
 this.options[this.selectedIndex].value);">
          <option value="blue">blue</option>
          <option value="green">green</option>
        </select>
        <small>
          <a href="http://www.brownhen.com/dom\_api\_top.html" id="sample">
            (sample link)</a
 ></small
 ><br />
      </form>
      <form>
        <input type="button" value="version" onclick="ver()" />
      </form>
    </div>
  </body>
</html>
```

단일 페이지(예를 들어, 웹 페이지의 색상에 영향을 주는 property 설정하는) 안의 수많은 interfaces 를 테스트하기 위해 설정 버튼, textfield, 또는 다른 HTML elements를 사용하여 유사한 테스트 페이지를 만들 수 있다. 아래의 스크린샷은 테스트를 위해 어떻게 interfaces를 그룹화하는지에 대한 아이디어를 제공하고 있다.

이 예제에서 드롭다운 메뉴는 웹 페이지에서 DOM 접근가능한 배경색상(`bgColor`), 하이퍼링크 색상(`aLink`), 텍스트 색상(`text`)을 동적으로 업데이트한다. 어떻게 자신의 test pages 를 디자인하더라도, interface 테스트는 DOM 을 효과적으로 사용하는 법을 배우는 데 매우 중요한 수단임을 명심하라.

Subnav
------

* DOM Reference
* Introduction to the DOM
* Events and the DOM (en-US)
* Examples


Examples - Web API

Examples
========

이 장에서는 DOM을 사용한 웹, XML 개발의 자세한 예제를 제공합니다. 예제는 문서의 객체를 조작하기 위해 가능한 JavaScript의 공통 API, 트릭, 패턴을 사용합니다.

예제 1: 높이와 너비
------------

아래의 예제는 다양한 면적의 이미지를 통해 `height` 와 `width` 속성을 사용하는 방법을 보여줍니다.

```html
<!doctype html>
<html lang="ko">
  <head>
    <title>width/height 예제</title>
    <script>
 function init() {
 var arrImages = new Array(3);

 arrImages[0] = document.getElementById("image1");
 arrImages[1] = document.getElementById("image2");
 arrImages[2] = document.getElementById("image3");

 var objOutput = document.getElementById("output");
 var strHtml = "<ul>";

 for (var i = 0; i < arrImages.length; i++) {
 strHtml +=
 "<li>image" +
 (i + 1) +
 ": height=" +
 arrImages[i].height +
 ", width=" +
 arrImages[i].width +
 ", style.height=" +
 arrImages[i].style.height +
 ", style.width=" +
 arrImages[i].style.width +
 "<\/li>";
 }
 strHtml += "<\/ul>";
 objOutput.innerHTML = strHtml;
 }
 </script>
  </head>
  <body onload="init();">
    <p>
      이미지 1: 높이, 너비, 스타일 없음
      <img id="image1" src="http://www.mozilla.org/images/mozilla-banner.gif" />
    </p>

    <p>
      이미지 2: 높이="50", 너비="500", 스타일 없음
      <img
 id="image2"
 src="http://www.mozilla.org/images/mozilla-banner.gif"
 height="50"
 width="500" />
    </p>
    <p>
      이미지 3: 높이, 너비 없음, 스타일="height: 50px; width: 500px;"
      <img
 id="image3"
 src="http://www.mozilla.org/images/mozilla-banner.gif"
 style="height: 50px; width: 500px;" />
    </p>
    <div id="output"></div>
  </body>
</html>
```

예제 2: 이미지 속성
------------

```html
<!doctype html>
<html lang="ko">
  <head>
    <title>이미지 border 수정</title>

    <script>
 function setBorderWidth(width) {
 document.getElementById("img1").style.borderWidth = width + "px";
 }
 </script>
  </head>

  <body>
    <p>
      <img
 id="img1"
 src="image1.gif"
 style="border: 5px solid green;"
 width="100"
 height="100"
 alt="border 테스트" />
    </p>

    <form name="FormName">
      <input
 type="button"
 value="border를 20px로 만들기"
 onclick="setBorderWidth(20);" />
      <input
 type="button"
 value="border를 5px로 만들기"
 onclick="setBorderWidth(5);" />
    </form>
  </body>
</html>
```

예제 3: 스타일 조작
------------

아래의 간단한 예제에서 HTML 요소의 일부 기본 스타일 속성들은 요소의 스타일 객체와 DOM에서 검색하고 설정할 수 있는 해당 객체의 CSS 스타일 속성을 사용하여 접근합니다. 이 경우 개별 스타일을 직접 조작합니다. 다음 예제(예제 4)에서는 stylesheet와 해당 규칙을 사용해 전체 문서의 스타일을 변경할 수 있습니다.

```html
<!doctype html>
<html lang="ko">
  <head>
    <title>색상 및 글꼴 크기 변경</title>

    <script>
 function changeText() {
 const p = document.getElementById("pid");

 p.style.color = "blue";
 p.style.fontSize = "18pt";
 }
 </script>
  </head>
  <body>
    <p id="pid" onclick="window.location.href = 'http://www.cnn.com/';">
      linker
    </p>

    <form>
      <p><input value="rec" type="button" onclick="changeText();" /></p>
    </form>
  </body>
</html>
```

예제 4: Stylesheet 사용
-------------------

`document` 객체의 `styleSheets` 속성은 그 문서에서 로드된 stylesheet 목록을 반환합니다. 이 예제에서처럼 stylesheet, 스타일, `CSSRule` (en-US) 객체를 사용해 이러한 stylesheet와 규칙에 개별적으로 접근할 수 있습니다. 다음 코드는 모든 스타일 규칙의 Selector를 콘솔에 출력합니다.

```js
var ss = document.styleSheets;

for (var i = 0; i < ss.length; i++) {
  for (var j = 0; j < ss[i].cssRules.length; j++) {
    dump(ss[i].cssRules[j].selectorText + "\n");
  }
}
```

다음과 같이 세가지 규칙이 정의된 stylesheet가 있는 경우, 스크립트의 결과물은 다음과 같습니다

```css
body {
  background-color: darkblue;
}
p {
  font-face: Arial;
  font-size: 10pt;
  margin-left: 0.125in;
}
#lumpy {
  display: none;
}
```

```
BODY
P
#LUMPY
```

예제 5: Event 전파
--------------

이번 예제에서는 DOM에서 어떻게 이벤트가 실행되고 처리되는지 매우 간단하게 알아보겠습니다. HTML 문서의 BODY가 로드되면, TABLE의 상단 행에 이벤트 수신기가 등록됩니다. 이벤트 수신기는 이벤트를 처리하기 위해 stopEvent 함수를 실행합니다. stopEvent 함수는 테이블의 하단 셀의 값을 변경합니다.

stopEvent는 이벤트 객체 메서드인 `event.stopPropagation`도 호출합니다. 이 메서드는 이벤트가 DOM으로 더 이상 버블링(bubbling)되지 않도록 합니다. 테이블이 클릭될 때 메시지를 표시해야 하는 `onclick` 이벤트 처리기가 있다는 것에 주의하세요. 하지만 stopEvent 메서드가 전파를 중지했기 때문에 테이블의 데이터가 업데이트된 후 이벤트 단계는 효과적으로 종료되고, 이를 확인하는 alert 창이 표시됩니다.

```html
<!doctype html>
<html lang="ko">
  <head>
    <title>이벤트 전파</title>

    <style>
 #t-daddy {
 border: 1px solid red;
 }
 #c1 {
 background-color: pink;
 }
 </style>

    <script>
 function stopEvent(event) {
 c2 = document.getElementById("c2");
 c2.innerHTML = "안녕하세요";

 // 이벤트가 t-daddy로 전파되지 않도록 합니다.
 ev.stopPropagation();
 alert("이벤트 전파가 중지되었습니다.");
 }

 function load() {
 elem = document.getElementById("tbl1");
 elem.addEventListener("click", stopEvent, false);
 }
 </script>
  </head>

  <body onload="load();">
    <table id="t-daddy" onclick="alert('안녕하세요.');">
      <tr id="tbl1">
        <td id="c1">1</td>
      </tr>
      <tr>
        <td id="c2">2</td>
      </tr>
    </table>
  </body>
</html>
```

Example 6: getComputedStyle
---------------------------

이번 예제에서는 `window.getComputedStyle` 메서드가 `style` 속성이나 JavaScript(예시: `elt.style.backgroundColor="rgb(173, 216, 230)"`)를 사용하지 않고 요소의 스타일을 가져오는 방법을 보여줍니다. 후자와 같은 유형의 스타일은 `elt.style` (en-US) 처럼 직접적인 속성으로 가져올 수 있습니다. 자세한 내용은 DOM CSS Properties List를 참고하세요.

`getComputedStyle()`은 `CSSStyleDeclaration` (en-US) 객체를 반환합니다. 다음 예제에서 알 수 있듯이 이 객체의 개별 스타일 속성은 `getPropertyValue()` (en-US) 메서드를 사용하여 참조할 수 있습니다.

```html
<!doctype html>
<html lang="ko">
  <head>
    <title>getComputedStyle 예제</title>

    <script>
 function cStyles() {
 const RefDiv = document.getElementById("d1");
 const txtHeight = document.getElementById("t1");
 const h\_style = document.defaultView
 .getComputedStyle(RefDiv, null)
 .getPropertyValue("height");

 txtHeight.value = h\_style;

 const txtWidth = document.getElementById("t2");
 const w\_style = document.defaultView
 .getComputedStyle(RefDiv, null)
 .getPropertyValue("width");

 txtWidth.value = w\_style;

 const txtBackgroundColor = document.getElementById("t3");
 const b\_style = document.defaultView
 .getComputedStyle(RefDiv, null)
 .getPropertyValue("background-color");

 txtBackgroundColor.value = b\_style;
 }
 </script>

    <style>
 #d1 {
 margin-left: 10px;
 background-color: rgb(173, 216, 230);
 height: 20px;
 max-width: 20px;
 }
 </style>
  </head>

  <body>
    <div id="d1"> </div>

    <form action="">
      <p>
        <button type="button" onclick="cStyles();">getComputedStyle</button>
        height<input id="t1" type="text" value="1" /> max-width<input
 id="t2"
 type="text"
 value="2" />
        bg-color<input id="t3" type="text" value="3" />
      </p>
    </form>
  </body>
</html>
```

Example 7: 이벤트 객체 속성 표시
-----------------------

이번 예제에서는 DOM 메서드를 이용해 `window.onload` (en-US) `event` 객체의 모든 속성과 값들을 테이블에 표시하는 방법과, 객체의 속성을 반복하여 값을 가져오는 `for...in` 루프의 유용한 사용법을 보여줍니다.

이벤트 객체들의 특성은 브라우저 마다 상당히 다르며, 표준 속성은 WHATWG DOM Standard에 있습니다. 하지만 많은 브라우저에서 이 기능을 크게 확장하고 있습니다.

아래의 코드를 빈 텍스트 파일에 넣고 여러 브라우저에서 로드하면 속성의 수와 이름들이 다르다는 사실에 놀랄 것입니다. 페이지에 일부 요소를 추가하고 다른 이벤트 처리기에서 이 함수를 호출할 수도 있습니다.

```html
<!doctype html>
<html lang="ko">
  <head>
    <meta charset="utf-8" />
    <title>이벤트 속성 표시</title>

    <style>
 table {
 border-collapse: collapse;
 }
 thead {
 font-weight: bold;
 }
 td {
 padding: 2px 10px 2px 10px;
 }

 .odd {
 background-color: #efdfef;
 }
 .even {
 background-color: #ffffff;
 }
 </style>

    <script>
 function showEventProperties(e) {
 function addCell(row, text) {
 const cell = row.insertCell(-1);
 cell.appendChild(document.createTextNode(text));
 }

 const event = e || window.event;
 document.getElementById("eventType").innerHTML = event.type;

 const table = document.createElement("table");
 const thead = table.createTHead();
 let row = thead.insertRow(-1);
 const labelList = ["#", "Property", "Value"];
 const len = labelList.length;

 for (let i = 0; i < len; i++) {
 addCell(row, labelList[i]);
 }

 var tbody = document.createElement("tbody");
 table.appendChild(tbody);

 for (var e in event) {
 row = tbody.insertRow(-1);
 row.className = row.rowIndex % 2 ? "odd" : "even";
 addCell(row, row.rowIndex);
 addCell(row, e);
 addCell(row, event[e]);
 }
 document.body.appendChild(table);
 }

 window.onload = (event) => {
 showEventProperties(event);
 };
 </script>
  </head>

  <body>
    <h1>DOM의 특성 <span id="eventType"></span> 이벤트 객체</h1>
  </body>
</html>
```

Example 8: DOM 테이블 인터페이스 사용하기
-----------------------------

DOM `HTMLTableElement` 인터페이스는 편리하게 테이블을 생성하고 조작해주는 메서드를 제공합니다. 자주 사용되는 메서드는 `HTMLTableElement.insertRow` (en-US)와 `HTMLTableRowElement.insertCell` (en-US)입니다.

아래 코드에서는 기존 테이블에 행과 셀을 추가하는 방법을 보여줍니다.

```html
<table id="table0">
  <tr>
    <td>Row 0 Cell 0</td>
    <td>Row 0 Cell 1</td>
  </tr>
</table>

<script>
 const table = document.getElementById("table0");
 const row = table.insertRow(-1);
 let cell;
 let text;

 for (var i = 0; i < 2; i++) {
 cell = row.insertCell(-1);
 text = "Row " + row.rowIndex + " Cell " + i;
 cell.appendChild(document.createTextNode(text));
 }
</script>
```

### 참고

* 테이블의 `innerHTML` 속성을 사용하여 테이블을 수정하면 안 됩니다. 단, 테이블 전체나 셀의 내용을 작성할 때는 이 속성을 사용할 수 있습니다.
* 만약 DOM의 핵심 메서드인 `document.createElement`와 `Node.appendChild`를 이용하여 행과 셀을 작성하는데 사용되는 경우, 인터넷 익스플로어(IE)에서는 해당 메서드를 tbody 요소에 추가해야 하지만 다른 브라우저는 테이블 요소에 추가할 수 있습니다(행은 마지막 tbody 요소에 추가됩니다).
* 테이블을 생성하고 수정하는 데 사용할 수 있는 더 많은 편리한 메서드가 table interface에 있습니다.


Using readable streams - Web API

Using readable streams
======================

JavaScript 개발자로서, 프로그래밍적으로 네트워크로부터 받은 데이터 스트림을 Chunk단위로 읽고 다루는 것은 매우 유용합니다! 그러나 어떻게 스트림 API의 Readable stream을 잘 사용할수 있을까요. 이번 내용은 그것을 설명하고 있습니다.

**참고:** This article assumes that you understand the use cases of readable streams, and are aware of the high-level concepts. If not, we suggest that you first read the Streams concepts and usage overview and dedicated Streams API concepts (en-US) article, then come back.

**참고:** If you are looking for information on writable streams try Using writable streams (en-US) instead.

Browser support
---------------

Firefox 65+ 와 크롬 42+ 에서 Fetch Body 객체를 스트림으로서 사용 할수 있고, custom readable 스트림을 만들수 있습니다. 현재 Pipe chains (en-US)의 경우 오직 크롬에서만 지원하고 있고 그 기능은 변경될 수 있습니다.

Finding some examples
---------------------

이번 내용과 관련한 많은 예제를 dom-examples/streams 에서 살펴볼수 있습니다. 이곳에서 모든 소스를 확인할수 있을 뿐만 아니라 예제 사이트 링크도 확인할수 있습니다.

Consuming a fetch as a stream
-----------------------------

Fetch API 는 네트워크를 통해 리소스를 가져오는 XHR의 현대적인 대안책 입니다. Fetch API의 수많은 이점 가운데 가장 훌륭한점은 최근 브라우저들이 Fetch Response를 Readable 스트림으로 이용할수 있게 하는 기능을 추가한것 입니다.

`Body` 믹스인은 `body` (en-US) 속성을 포함하고 있습니다. 그리고 이 `body` (en-US) 속성은 `body` (en-US)의 내용을 Readable 스트림으로 노출시키는 간단한 getter 입니다. 이 `Body` 믹스인은 `Request`와 `Response` 인터페이스로부터 구현 되며, 따라서 두 경우 모두 사용 할수 있습니다. 다만 Response body의 스트림을 사용하는 것이 조금더 명확합니다.

우리의 Simple stream pump예시에서 보여주듯(see it live also), Readable 스트림을 노출시키기 위해서는 단지 Response의 body 속성에 접근하기만 하면 됩니다.

```js
// 오리지널 이미지를 Fetch 함
fetch("./tortoise.png")
  // body 를 ReadableStream으로 가공함
  .then((response) => response.body);
```

이것은 우리에게 `ReadableStream` 객체를 제공해 줍니다.

### Attaching a reader

이제 우리는 스트림화된 body를 가지고 있으며, 이 스트림을 읽기 위해서는 리더기를 붙여야 합니다. 이 작업은 `ReadableStream.getReader()` (en-US) 메서드를 사용하여 처리합니다.

```js
// 오리지널 이미지를 Fetch 함
fetch('./tortoise.png')
// body 를 ReadableStream으로 가공함
.then(response => response.body)
.then(body => {
  const reader = body.getReader();
```

이 메서드를 실행하면 리더기가 생성되고 이 리더기를 스트림에 고정(locks) 시킵니다. `ReadableStreamDefaultReader.releaseLock()` (en-US) 메서드를 사용하는등 이 고정(locks)을 풀기전 까지는(released), 그 어떤 다른 리더기들도 이 스트림을 읽을수 없습니다.

또한 `response.body` 는 동기이므로 굳이 프로미스를 사용할 필요가 없으며, 위의 예시를 한번의 스텝으로 줄일수 있다는 점에 주목해 주십시요

```js
// 오리지널 이미지를 Fetch 함
  fetch('./tortoise.png')
  // body 를 ReadableStream으로 가공함
  .then(response => {
    const reader = response.body.getReader();
```

### Reading the stream

이제 우리는 우리의 리더기를 추가하였습니다. 우리는 리더기의 `ReadableStreamDefaultReader.read()` (en-US) 메서드를 사용하여 스트림으로 부터 data chunk들을 읽을수 있게 되었습니다.

정확하게는 이 메서드는 각 스트림으로부터 하나의 data chunk를 읽습니다. 그리고 이러한 data chunk는 우리가 원하는대로 사용할 수 있습니다.

예를 들어 우리의 Simple stream pump example에서는 `ReadableStreamDefaultReader.read()` (en-US) 사용하여 Data Chunk를 새로운 커스텀 `ReadableStream`에 집어 넣고 있습니다. 그리고 만약 읽을수 있는 다음 Data Chunk가 있다면, `ReadableStreamDefaultReader.read()` (en-US) 를 다시 사용하여 다음 Data Chunk를 커스텀 `ReadableStream`에 집어 넣습니다. 더이상 읽을수 있는 Data Chunk가 없다면, 그 커스텀 `ReadableStream`(우리는 이 새로운 Readable 스트림에 대해 다음 섹션에서 다 자세히 살펴 볼것 입니다.)을 통해 새로운 `Response` 객체를 생성합니다. 그 다음 이 `Response` 객체를 `Blob` 형태로 변환하고 이 `Blob` 으로 부터 `URL.createObjectURL()` 메서드를 사용하여 URL 객체를 생성합니다. 마지막으로 이 URL객체를 {htmlelement("img")}} 에 적용하여 이미지를 보여줌으로서 fetch된 오리지널 이미지를 효과적으로 복사하는 것입니다.

```js
  return new ReadableStream({
    start(controller) {
      return pump();
      function pump() {
        // 스트림의 다음 Chunk에 대한 액세스를 제공하는 psomise를 리턴한다.
        return reader.read().then(({ done, value }) => {
          // 더이상 읽을 데이터 조각이 없을때 스트림을 닫는다
          if (done) {
              controller.close();
              return;
          }
          // 데이터 조각을 새로운 스트림(새로 만드는 커스텀 스트림)에 넣는다.
          controller.enqueue(value);
          return pump();
        });
      }
    }
  })
})
.then(stream => new Response(stream))
.then(response => response.blob())
.then(blob => URL.createObjectURL(blob))
.then(url => console.log(image.src = url))
.catch(err => console.error(err));
```

어떻게 `read()` 가 사용되었는지 자세히 들여다 봅시다. 위 예제의 `pump()` 함수는 제일먼저 `read()` 를 실행하였습니다. 그리고 이 `read()` 는 스트림으로부터 읽어 들인 내용의 결과를 `{ done, value }` 의 형식으로 가지고 있는 pomise를 반환합니다.

```js
return reader.read().then(({ done, value }) => {
```

스트림으로부터 읽어 들인 내용은 아래 3가지 타입이 있습니다.

* chunk를 아직 읽을수 있는 경우에 프로미스는 `{ value: theChunk, done: false }` 객체와 함께 fulfill 됩니다.
* 스트림이 닫힌 경우에 프로미스는 `value: undefined, done: true }` 객체와 함께 fulfill 됩니다.
* 스트림에서 애러가 발생한 경우 promise는 관련 애러와 함께 reject 됩니다.

다음으로 우리는 `done` 이 `true` 인지 아닌지 확인해 봐야 합니다.

만약 `done` 이 `true` 라면 더이상 읽어들일 chunk가 없다는 뜻입니다. 따라서 우리는 함수 밖으로 빠져 나가야 하고 `ReadableStreamDefaultController.close()` (en-US) 를 통하여 커스텀 스트림을 닫아야 합니다.

```js
if (done) {
  controller.close();
  return;
}
```

**참고:** 여기서 사용한 `close()` 는 새로만든 커스텀 스트림의 일부이며 오리지널 스트림의 것이 아닙니다. 커스텀 스트림에 대해서는 다음섹션에서 더 자세히 살펴 볼 예정입니다.

만약 `done` 이 `true` 가 아니라면, 우선 읽어 드린 Chunk를 처리하고 (`value` 속성), `pump()` 함수를 재귀적으로 다시 호출 함으로서 다음 chunk를 읽어 드립니다.

```js
// 다음 data chunk를 새로운 readable 스트림에 집어 넣음
controller.enqueue(value);
return pump();
```

다음은 스트림 리더기를 사용할때의 기본적인 패턴 입니다.

1. 스트림을 읽음으로서 실행되는 함수를 작성합니다.
2. 만약 읽을수 있는 스트림이 더이상 없다면, 함수를 리턴 시킵니다.
3. 만약 읽을수 있는 스트림이 아직 남아 있다면, 우선 읽어 드린 chunk를 처리하고, 다음 chunk를 읽어 드리기 위해 함수를 다시 실행합니다.
4. 더이상 읽을수 있는 스트림이 없을때까지 함수를 재귀적으로 실행하고, 최종적으로 읽을수 있는 스트림이 없을경우 2번 Step을 따릅니다.
Creating your own custom readable stream
----------------------------------------

두번째 파트에서 사용했던 Simple stream pump example 예제에서 우리는 fetch body로 부터 읽어드린 이미지에 대한 data를 우리가 자체적으로 만든 커스텀 readable 스트림에 다시 옮겨 심었습니다. 그럼 어떻게 스트림을자체적으로 만들수 있을까요? 우리는 `ReadableStream()`생성자 함수를 통해 커스텀 readable 스트림을 만들 수 있습니다.

### The ReadableStream() constructor

Fetch와 같이 브라우저에서 스트림을 제공할때 그것을 읽어 들이는 일은 쉬운 일입니다. 그러나 때때로 새로운 커스텀 스트림을 만들고 이것을 data chunk들로 채워넣어야 하는 경우가 있습니다. `ReadableStream()` (en-US)을 아래의 구문과 같이 사용함으로서 이것을 가능하게 할수 있습니다. 구문이 처음에는 다소 복잡해 보일수 있습니다만, 실제로는 그렇게 복잡하지 않습니다.

기본적인 핵심 구문은 다음과 같습니다.

```js
const stream = new ReadableStream({
  start(controller) {

  },
  pull(controller) {

  },
  cancel() {

  },
  type,
  autoAllocateChunkSize
}, {
  highWaterMark,
  size()
});
```

생성자 함수는 두개의 객체를 인자로 받습니다. 첫번째 인자는 필수 값이며, 이것은 우리가 읽어 들일 기본 소스의 모델을 JavasScrip 적으로 생성 합니다. 두번째 인자는 옵션 값이며, 이것은 커스텀 스트림에 사용할 커스텀 queuing 전략을 수립하게 합니다. 두번째 인자의 경우 매우 드물게 설정하는 값이므로 지금은 첫번째 인자에 집중하도록 하겠습니다.

첫번째 인자인 객체는 5개의 맴버를 가질수 있으며, 제일 첫번째 맴버만 필수 입니다.

1. `start(controller)` — `ReadableStream` 이 생성되자 마자 딱 한번만 호출 되는 메서드 입니다. 이 메서드에는 스트림을 기능을 설정할 수 있는 코드가 포함되어야 합니다. 예를 들면 데이터 생성을 시작한다거나 아니면 소스에 접근하여 데이터를 가져오는 코드등이 들어가게 될것입니다.
2. `pull(controller)` — 이 메서드는 스트림 내부의 queue가 가득 찰때까지 반복적으로 호출 됩니다. 더 많은 청크가 큐에 들어갈 때 스트림을 제어하는 데 사용할 수 있습니다.
3. `cancel()` — 이 메서드는 스트림이 캔슬될때 호출 됩니다 (예를 들어 {domxref("ReadableStream.cancel()")}} 이 호출 되었을때). 메서드의 내용은 스트림 소스에 대한 액세스를 해제하는 데 필요한 모든 것을 수행해야합니다.
4. `type` and `autoAllocateChunkSize` — 스트림이 바이트 스트림임을 나타 내기 위해 사용됩니다. 바이트 스트림은 목적과 사용 사례가 일반 (기본) 스트림과 약간 다르므로 향후 자습서에서 별도로 다룰 것입니다. 또한 아직 어느 곳에서도 구현되지 않았습니다.

simple example code 를 다시한번 살펴보면, `ReadableStream()` 생성자가 `start()` 메서드 단 하나만 가지고 있다는 것을 알아챌수 있을 것 입니다. 이 `start()` 메서드 fetch된 스트림으로 부터 데이터를 읽어 들이고 있습니다.

```js
  return new ReadableStream({
    start(controller) {
      return pump();
      function pump() {
        return reader.read().then(({ done, value }) => {
          // 더이상 읽을수 있는 data가 없다면 스트림을 닫는다
          if (done) {
            controller.close();
            return;
          }
          // 데이터 조각을 새로운 스트림(새로 만드는 커스텀 스트림)에 넣는다.
          controller.enqueue(value);
          return pump();
        });
      }
    }
  })
})
```

### ReadableStream controllers

ReadableStrem() 생성자에 인자로 전달된 객체안의 `start()` 와 `pull()` 메서드에 controller라는 인자가 전달되는 것을 볼수 있습니다. 이것은 `ReadableStreamDefaultController` (en-US) 클래스의 인스턴스이며 우리의 스트림을 제어하는데 사용 됩니다.

우리의 예제에서, 우리는 fetch된 body로부터 chunk의 값을 읽은 뒤 그 값을 커스텀 스트림에 집어 넣기 위해 Controller의 `enqueue()` (en-US) 메서드를 상용하고 있습니다.

또한, fetch된 body를 읽어 들이는 것이 끝나면 컨트롤러의 `close()` (en-US)를 사용하여 커스텀 스트림을 닫습니다. 이때 이미 삽입된 chunk들은 여전히 읽을수 있지만 새로운 chunk는 집어 넣을수 없습니다. 그리고 읽는 것이 모두 긑나면 스트림은 닫힙니다.

### Reading from custom streams

우리의 Simple stream pump example 에서, 우리는 {domxref("Response.Response", "Response")}} 생성자 함수에 우리가 만든 커스텀 readable 스트림을 인자로 전달하였으며 그렇게 생성된 response 인스턴스를 `blob()` 으로 사용 하였습니다.

```js
.then(stream => new Response(stream))
.then(response => response.blob())
.then(blob => URL.createObjectURL(blob))
.then(url => console.log(image.src = url))
.catch(err => console.error(err));
```

그러나 커스텀 스트림은 여전히 `ReadableStream` 인스턴스입니다. 즉, 우리는 여전히 리더기를 커스텀 스트림에 붙일수 있다는 뜻입니다. 예를 들어 Simple random stream demo (see it live also) 를 살펴 보십시요. 이 예제에서는 커스텀 스트림을 생성한후, 랜덤 문자열을 생성된 커스텀 스트림에 집어 넣습니다. 그 후 문자열 생성 중지 버튼을 눌렀을때 커스텀 스트림에 집어 넣었던 랜덤 문자열을 커스텀 스트림으로 부터 다시 읽어 옵니다.

**Note:** `FetchEvent.respondWith()` (en-US) 메서드를 사용하여 스트림을 다루기 위해서는 스트림에 삽입된 내용이 만드시 `Uint8Array` 이어야 합니다. (`TextEncoder` (en-US) 등을 사용)

Simple random stream demo (see it live also) 에서 커스텀 스트림 생성자 함수는 `start()` 메서드를 가지고 있으며, 이 메서드는 `WindowTimers.setInterval()` (en-US) 을 사용하여 매초마다 새로운 랜덤 문자열을 생성하고 이 문자열을 `ReadableStreamDefaultController.enqueue()` (en-US) 를 사용하여 스트림안에 넣습니다. 만약 문자열 생성 중지 버튼을 누른다면 이 interval 이 취소됨과 동시에 `readStream()` 함수를 호출하여 스트림으로 부터 스트림에 넣은 문자열 전부를 읽어 드립니다. 이때 스트림에 chunk data(여기서는 랜덤 문자열)를 넣는 것도 중단 했기때문에 스트림을 닫습니다.

```js
const stream = new ReadableStream({
  start(controller) {
    interval = setInterval(() => {
      let string = randomChars();
      // Add the string to the stream
      controller.enqueue(string);
      // show it on the screen
      let listItem = document.createElement("li");
      listItem.textContent = string;
      list1.appendChild(listItem);
    }, 1000);
    button.addEventListener("click", function () {
      clearInterval(interval);
      readStream();
      controller.close();
    });
  },
  pull(controller) {
    // We don't really need a pull in this example
  },
  cancel() {
    // This is called if the reader cancels,
    // so we should stop generating strings
    clearInterval(interval);
  },
});
```

스트림에 넣은 문자열 전부를 읽고 다루기 위해 만든 커스텀 함수인 `readStream()` 함수를 살펴보면, 우선 `ReadableStream.getReader()` (en-US)를 사용하여 리더기를 스트림에 고정시키는 것을 볼수 있습니다. 그 후 앞서 살펴봤던 패턴과 마찬가지로 `read()` 를 사용하여 chunk를 읽어 들이고 `done` 이 `true` 인지 아닌지 확인 합니다. 만약 `true` 이면 `readStream()` 함수의 프로세스를 끝내버리고 그렇지 않으면 읽어드린 chunk를 후속처리한 후 `read()` 를 재귀적으로 실행합니다.

```js
function readStream() {
  const reader = stream.getReader();
  let charsReceived = 0;

  // read() returns a promise that resolves
  // when a value has been received
  reader.read().then(function processText({ done, value }) {
    // Result objects contain two properties:
    // done - true if the stream has already given you all its data.
    // value - some data. Always undefined when done is true.
    if (done) {
      console.log("Stream complete");
      para.textContent = result;
      return;
    }

    charsReceived += value.length;
    const chunk = value;
    let listItem = document.createElement("li");
    listItem.textContent =
      "Read " + charsReceived + " characters so far. Current chunk = " + chunk;
    list2.appendChild(listItem);

    result += chunk;

    // Read some more, and call this function again
    return reader.read().then(processText);
  });
}
```

### Closing and cancelling streams

우리는 이미 앞서 스트림을 닫는 메서드인 `ReadableStreamDefaultController.close()` (en-US) 를 살펴보았습니다. 이미 언급했다 시피, 스트림이 닫혔다 하더라고 이미 들어가 있는 chunk는 읽을 수 있습니다.

만약 스트림을 완벽하게 제거하고 삽입된 모든 chunk를 날리고 싶다면, `ReadableStream.cancel()` (en-US) 또는 `ReadableStreamDefaultReader.cancel()` (en-US) 메서드를 사용 하면 됩니다.

Teeing a stream
---------------

때로는 하나의 스트림을 동시 두번 읽어들여야 할 경우가 있습니다. `ReadableStream.tee()` (en-US) 메서드가 이를 가능하게 합니다. `ReadableStream.tee()` (en-US) 메서드는 두개의 독립적인 카피된 스트림을 가지고 있는 배열을 제공합니다. 이 카피된 두개의 스트림은 두개의 독립적인 리더기로 각각 읽어 들일 수 있습니다.

이런 경우는 아마 ServiceWorker 안에서 필요할 것입니다. 만약 서버로부터 fetch된 response를 브라우저에도 전달하고 서비스 워커 캐시에도 전달해야 하다면 하나의 스트림에 대해 두개의 카피본이 필요 할 것입니다. 왜냐하면 response body (Readablestream)는 단 한번만 사용될 수 있고 하나의 Readablestream은 하나의 리더기만 붙을수 있기 때문입니다.

위 내용에 대한 예제를 Simple tee example (see it live also)에서 살펴 볼수 있습니다. 이 예제는 랜덤 문자열 생성 버튼에 대한 이벤트가 없다는 점과, 이 예제에서의 스트림은 teed되어 두개의 스트림이 두개의 리더기로 읽어진다는 점만 제외하면 앞서 살펴본 Simple random stream 예제와 매우 유사하게 동작합니다.

```js
function teeStream() {
  const teedOff = stream.tee();
  readStream(teedOff[0], list2);
  readStream(teedOff[1], list3);
}
```

Pipe chains
-----------

One very experimental feature of streams is the ability to pipe streams into one another (called a pipe chain (en-US)). This involves two methods — `ReadableStream.pipeThrough()` (en-US), which pipes a readable stream through a writer/reader pair to transform one data format into another, and `ReadableStream.pipeTo()` (en-US), which pipes a readable stream to a writer acting as an end point for the pipe chain.

This functionality is at a very experimental stage and is subject to change, so we have no explored it too deeply as of yet.

We have created an example called Unpack Chunks of a PNG (see it live also) that fetches an image as a stream, then pipes it through to a custom PNG transform stream that retrieves PNG chunks out of a binary data stream.

```js
// Fetch the original image
fetch("png-logo.png")
  // Retrieve its body as ReadableStream
  .then((response) => response.body)
  // Create a gray-scaled PNG stream out of the original
  .then((rs) => logReadableStream("Fetch Response Stream", rs))
  .then((body) => body.pipeThrough(new PNGTransformStream()))
  .then((rs) => logReadableStream("PNG Chunk Stream", rs));
```

Summary
-------

That explains the basics of "default" readable streams. We'll explain bytestreams in a separate future article, once they are available in browsers.


table.width - Web API

table.width
===========

### 요약

**width**는 바라는 테이블의 너비를 지정합니다.

### 구문

```js
HTMLTableElement.width = width;
var width = HTMLTableElement.width;
```

`width`가 픽셀수나 퍼센트 값으로 너비를 나타내는 문자열인 곳.

### 예제

```js
mytable.width = "75%";
```

명세서
---

| Specification |
| --- |
| HTML Standard # dom-table-width |


BroadcastChannel: message event - Web API

BroadcastChannel: message event
===============================

Baseline 2022
-------------

Newly availableSince March 2022, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.

* Learn more
* See full compatibility
* Report feedback
`message` 이벤트는 해당되는 채널에 메시지가 도착할 시, `BroadcastChannel` 객체에서 발생합니다.

구문
--

`addEventListener()` 같은 메서드에서 이벤트 이름을 사용하거나, 이벤트 처리기 속성을 설정하세요.

```js
addEventListener("message", (event) => { })
onmessage = (event) => { }
```

이벤트 타입
------

`MessageEvent`. `Event`를 상속합니다.

Event

MessageEvent이벤트 속성
------

아래 나열된 속성 외에도, 부모 인터페이스인 `Event`의 속성들을 사용할 수 있습니다.

`data` (en-US) 읽기 전용

메시지 발신자가 보낸 데이터.

`origin` (en-US) 읽기 전용

메시지 발신자의 출처를 나타내는 문자열.

`lastEventId` (en-US) 읽기 전용

이벤트의 고유 ID를 나타내는 문자열.

`source` (en-US) 읽기 전용

WindowProxy, `MessagePort` (en-US), 또는 `ServiceWorker` 객체 등 메시지 발신자를 나타내는 메시지 이벤트 출처.

`ports` (en-US) 읽기 전용

메시지가 전송되는 채널과 연결된 포트를 나타내는 `MessagePort` (en-US) 객체의 배열(예: 체널 내 메시징, 공유된 맥락에 메시지를 보낼 때 등).

예제
--

이 예제에서는 사용자가 버튼을 클릭했을 때 `<textarea>` (en-US)의 내용을 발신하는 "발신자" `<iframe>` (en-US)과, 발신된 메시지를 수신하고 그 결과를 `<div>` (en-US) 요소에 기록하는 "수신자" iframe이 있습니다.

### 발신자
```

<h1>Sender</h1>
<label for="message">Type a message to broadcast:</label><br />
<textarea id="message" name="message" rows="1" cols="40">Hello</textarea>
<button id="broadcast-message" type="button">Broadcast message</button>
```

```
body {
  border: 1px solid black;
  padding: 0.5rem;
  height: 150px;
  font-family: "Fira Sans", sans-serif;
}

h1 {
  font:
    1.6em "Fira Sans",
    sans-serif;
  margin-bottom: 1rem;
}

textarea {
  padding: 0.2rem;
}

label,
br {
  margin: 0.5rem 0;
}

button {
  vertical-align: top;
  height: 1.5rem;
}
```

```js
const channel = new BroadcastChannel("example-channel");
const messageControl = document.querySelector("#message");
const broadcastMessageButton = document.querySelector("#broadcast-message");

broadcastMessageButton.addEventListener("click", () => {
  channel.postMessage(messageControl.value);
});
```

### 수신자 1
```

<h1>Receiver 1</h1>
<div id="received"></div>
```

```
body {
  border: 1px solid black;
  padding: 0.5rem;
  height: 100px;
  font-family: "Fira Sans", sans-serif;
}

h1 {
  font:
    1.6em "Fira Sans",
    sans-serif;
  margin-bottom: 1rem;
}
```

```js
const channel = new BroadcastChannel("example-channel");
channel.addEventListener("message", (event) => {
  received.textContent = event.data;
});
```

### 수신자 2
```

<h1>Receiver 2</h1>
<div id="received"></div>
```

```
body {
  border: 1px solid black;
  padding: 0.5rem;
  height: 100px;
  font-family: "Fira Sans", sans-serif;
}

h1 {
  font:
    1.6em "Fira Sans",
    sans-serif;
  margin-bottom: 1rem;
}
```

```js
const channel = new BroadcastChannel("example-channel");
channel.addEventListener("message", (event) => {
  received.textContent = event.data;
});
```

### 결과

명세서
---

| Specification |
| --- |
| HTML Standard # event-message |
| HTML Standard # handler-broadcastchannel-onmessage |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

같이 보기
-----

* 연관된 이벤트: `messageerror`


BroadcastChannel.postMessage() - Web API

BroadcastChannel.postMessage()
==============================

Baseline 2022
-------------

Newly availableSince March 2022, this feature works across the latest devices and browser versions. This feature might not work in older devices or browsers.

* Learn more
* See full compatibility
* Report feedback
**`BroadcastChannel.postMessage()`** 는 모든 종류의 `Object`가 될 수 있는 메세지를 동일한 출처를 가진 모든 브라우징 맥락의 각 수신기에 보냅니다. 메세지는 채널에 바인딩된 각 `BroadcastChannel`을 대상으로 하는 'message' 이벤트로 전송됩니다.

**참고:** 이 기능은 Web Worker에서 사용할 수 있습니다

구문
--

```js
channel.postMessage(message)
```

### 매개변수

`message`

다른 창으로 전송될 데이터입니다. 데이터는 structured clone 알고리즘을 통해 직렬화됩니다. 즉, 다양한 데이터 객체를 직접 직렬화하지 않고도 안전하게 전송할 수 있습니다.

### 반환 값

없음.

### 예외

`InvalidStateError` `DOMException` (en-US)

`BroadcastChannel`이 이미 닫힌 경우 발생합니다.

`DataCloneError` `DOMException` (en-US)

데이터를 직렬화할 수 없는 경우 발생합니다.

명세서
---

| Specification |
| --- |
| HTML Standard # dom-broadcastchannel-postmessage-dev |

브라우저 호환성
--------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

같이 보기
-----

* `BroadcastChannel.postMessage()`가 속한 인터페이스, `BroadcastChannel`.