HTML Canvasのインターフェイスのまとめ #1

HTMLのCanvasを触れ始めたので備忘録として記事に残しておきます。

# 前置き

フロントエンドとかを専門（生業）としているわけではないので、知識や理解が浅い点などはご容赦ください。

# 本記事で触れること

CanvasのfillやlineToなどの各描画のインターフェイスについて触れていきます。画像処理やアニメーション、テキスト関係などは今回は触れません。また、記事にしていて描画関係だけでもかなり長くなったので全ては触れずに記事を分割したりしようと思います（本記事は1つ目の記事に該当します）。

# HTML Canvasって何？（+Canvasの特徴）

- HTMLとJavaScriptを使って描画処理や画像変換などを行える機能です。
- SVGとかでは描画領域が巨大になっても負荷になりにくい（一方で非常に細かい描画で負荷になりやすい）一方でCanvasは描画の範囲が負荷になり、細かい描画とかでは負荷になりにくい特徴を持ちます。つまり描画サイズはそこまで大きくないものの要素が膨大な場合のグラフ描画とかに（SVGよりも）向いています。
- その他ピクセル単位で描画データの加工なども行えるため、フォトショップで昔からあるようなフィルターのような制御も行うことができます。

※SVGに関しては以前以下の記事などでいくつか記事にしているので必要に応じてそちらをご確認ください。

https://qiita.com/simonritchie/items/16bfa1f9f80db10cb5ae

# 本記事で使うもの

- HTMLと若干のCSS
- JavaScript（TypeScriptやライブラリなどは今回触れません）

※CSSとJavaScriptは手間なので今回は直接HTML内で記述していきます。
※普段Phthonなどばかり書いていて設定の切り替えが手間なのでインデントはスペース2つではなく4つのまま進めていきます。

# Canvas最初の一歩

ごくごくシンプルな例でCanvasを使い始めてみます。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");
        context.fillStyle = "#00aaff";
        context.font = "30px Arial";
        context.fillText("Hello World!", 40, 40);
    </script>
</html>
```

上記のHTMLをブラウザで開いてみると以下のようにテキストが表示されることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/5e408999-85d2-2137-e672-6c03a70ab0f4.png)

Chromeなどのブラウザ上で、テキストの上で右クリックしてみると通常のコンテキストメニューではなく「名前を付けて画像を保存」といったように画像ファイルのようにCanvas用のコンテキストメニューになっていることを確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/881c063c-60fd-6d46-22a6-035342beeed9.png)

それぞれの記述を1つ1つ見ていきます。

```html
        <canvas id="canvas" width="500" height="350">
        </canvas>
```

まずは上記のHTMLタグ部分ですが、このタグがCanvas要素となります。幅や高さはCSSではなくHTMLタグに直接指定する方が推奨されるようです（微妙にCSSなどで設定した場合と挙動が変わります）。

```js
        let canvas = document.getElementById("canvas");
```

続いて上記のjsでCanvas要素を取得しています。これはCanvasとか関係無く一般的なjsの記述なので説明は割愛します。

```js
        let context = canvas.getContext("2d");
```

js上で取得したCanvas要素はgetContextメソッドを持っています。このメソッドで描画制御など諸々を制御するためのコンテキストのインスタンスを取得することができます。

引数に対象のコンテキストの種類を指定します。3Dのものなどもあるようですが今回は2Dのみ触れるため2dと引数に指定しています。

```js
        context.fillStyle = "#00aaff";
        context.font = "30px Arial";
```

上記の部分では描画のスタイル設定を行っています。fillStyleは塗りの色設定、fontでは文字サイズとフォントの種類を指定しています。このようにcontextのインスタンスでは各描画の設定を行うことができます（描画設定だけでなく、後述するように実際の描画もcontextインスタンスを用いて行います）。

```js
        context.fillText("Hello World!", 40, 40);
```

最後に上記のようにfillTextメソッドで文字を描画しています。第一引数に描画する文字列、第二引数にX座標、第三引数にY座標といったように指定します（fillTextなどのテキスト関係の詳しい説明は他の記事で触れていこうと思います）。

# 各描画設定

## 描画設定の引き継ぎについて

各描画設定は基本的に同じCanvasのコンテキストのインスタンスであれば引き継がれます。例えば一度塗りの色を設定して次に四角を描画、その次に円を描画・・・とすれば四角も円も同じ塗りの色となります。

## fillStyle設定

fillStyle属性では塗りの色を設定することができます。`#00aaff`といったような16進数の設定や`blue`といった色の文字列、`rgba(255, 255, 255, 0.8)`といったようなRGBA表記などCSSのように色々指定できます。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        context.fillStyle = "#0af";
        context.fillRect(50, 50, 50, 50);

        context.fillStyle = "red";
        context.fillRect(150, 50, 50, 50);

        context.fillStyle = "rgba(100, 100, 100, 0.5)";
        context.fillRect(250, 50, 50, 50);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e4b33c12-100d-020f-2c7c-686080469fa3.png)

## グラデーション設定（createLinearGradientとaddColorStopメソッド）

※Canvasの円形のグラデーションは結構設定と挙動が独特・・・と感じたのと個人的な利用機会は少なそうな気がしているため線形のグラデーションのみ触れていきます。

グラデーション設定は単純な単色の塗りの設定よりもコードが複雑になります。グラデーションを設定するには以下のような手順が必要になります。

- createLinearGradient メソッドでグラデーション設定用の CanvasGradient クラスのインスタンスを作成します。
- 作成した CanvasGradient クラスのインスタンスの addColorStop メソッドでグラデーションの色の変化点を設定できるのでそれを複数回呼んで色と位置を設定します。
- 作成した CanvasGradient クラスのインスタンスをコンテキストの fillStyle 属性に設定します。
- 任意の図形の描画などを行います。

createLinearGradient メソッドの第一引数はグラデーションの開始位置のX座標、第二引数はグラデーション開始位置のY座標、第三引数はグラデーション終了位置のX座標、第四引数はグラデーション終了位置のY座標となります。後に続く描画処理と座標が一致していないと想定したグラデーションの描画になりません。

addColorStop メソッドでは第一引数にはグラデーションの位置の比率値（0.0～1.0）、第二引数にグラデーションの色の文字列を指定します。第一引数の比率は0.0でグラデーション開始位置、0.5でグラデーションの中央位置、1.0でグラデーションの終了位置となります。

以下の例ではX座標が0～100の位置でシアン（`#0af`）からマゼンタ（`#f0a`）へのグラデーションを addColorStop メソッドで設定しています。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        let gradient = context.createLinearGradient(0, 0, 100, 0);
        gradient.addColorStop(0.0, '#0af');
        gradient.addColorStop(1.0, '#f0a');

        context.fillStyle = gradient
        context.fillRect(0, 0, 100, 100);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/36c09998-0c23-c609-e96f-f75c732fdedd.png)

注意点としてグラデーションの座標設定と実際の描画の座標がずれていると想定したグラデーションになりません。

たとえば以下のようにグラデーションの開始X座標が0、終了X座標が100となっている一方で四角の描写はXの開始座標が75・・・となっているとごく一部のグラデーションしか四角に反映されません。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        let gradient = context.createLinearGradient(0, 0, 100, 0);
        gradient.addColorStop(0.0, '#0af');
        gradient.addColorStop(1.0, '#f0a');

        context.fillStyle = gradient
        context.fillRect(75, 0, 100, 100);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/2f7ff82e-ec83-fe33-7f63-c14e2f7dd0b5.png)

## パターンによる塗り設定

※動画の要素もパターンに指定できるようですが個人的にひとまずは使うことは無さそうに思えたので動画関係はスキップします。

### 画像でパターンを指定する場合

まずは画像のパターンを塗りに設定するケースに触れていきます。サンプルとして以下の画像を`kikubishi_s.jpg`という名前でローカルに配置している想定で使用していきます。

![kikubishi_s.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/76c4edfe-9536-f5ab-e211-87e4a08e0306.jpeg)

※以下のフリー素材を使用させていただいています。

http://www.pattern-sozai.website/data/3013.php

画像のパターンを塗りに設定するには以下の手順で設定する形となります。

- 対象の画像の読み込み設定を行います。
- 画像の読み込みが終わったらコンテキストの createPattern メソッドでパターンのインスタンスを作成します。
- 作成したパターンのインスタンスを fillStyle 属性に指定します。
- 任意の描画処理を行います。

createPattern メソッドの第一引数には画像のインスタンス、第二引数には繰り返し設定の文字列の指定が必要です。第二引数の繰り返し設定は以下のような値を設定できます。

- repeat: 縦と横方向両方に対して繰り返しのパターンを設定します。
- repeat-x: 横方向のみ繰り返しのパターンを設定します。
- repeat-y: 縦方向のみ繰り返しのパターンを設定します。
- no-repeat: 繰り返しを行わず画像を1回のみ塗りに設定します。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        let image = new Image();
        image.src = "kikubishi_s.jpg";
        image.onload = function() {
            let pattern = context.createPattern(image, 'repeat');
            context.fillStyle = pattern;
            context.fillRect(0, 0, 500, 350);
        };
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/dad74cdf-1278-0b0c-cb4a-dd5e0df63820.png)

画像のパターンで四角を描画することができました。以下にコードを補足します。

まずは画像の読み込みですが、JavaScript上ではImageクラスで行うことができます。srcで画像のパスを指定し、onloadで関数を指定することで画像読み込み時のイベントを設定することができます。パターン関係の制御は画像の読み込みが終わってから実行する必要があるためパターン関係の制御はそのイベントの関数内で実施しています。

後はその関数内で createPattern メソッドでパターンのインスタンスを生成し、そのパターンを fillStyle 属性に設定して四角を描画しています。

上記の例では createPattern の第二引数に`repeat`と指定しているため縦横両方の方向にパターンが繰り返し描画されます。

### 他のCanvasでパターンを指定する場合

画像（Imageクラス）などの代わりに別のCanvasを指定することもできます。その場合には createPattern メソッドの第一引数に画像の代わりに他のCanvasのインスタンスを指定するだけで対応することができます。

以下の例では patternCanvas という変数名で別のパターン用のCanvasを追加しています。幅50高さ50のサイズで、Canvas内で余白10サイズ30で四角を描画しています（`fillRect(10, 10, 30, 30)`）。また、パターンのCanvas自体は表示しなくて良いので`display: none;`のCSSを指定して非表示にしています。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
        <canvas id="pattern-canvas" width="50" height="50"
        style="display: none;">
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        let patternCanvas = document.getElementById("pattern-canvas");
        let patternContext = patternCanvas.getContext("2d");
        patternContext.fillStyle = "#0af";
        patternContext.fillRect(10, 10, 30, 30);

        let pattern = context.createPattern(patternCanvas, "repeat");
        context.fillStyle = pattern;
        context.fillRect(0, 0, 500, 350);
    </script>
</html>
```

結果的に patternCanvas の四角を繰り返したパターンでCanvasの塗りが設定されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/592b4f67-91ea-8ddc-60dd-6a0326237480.png)

## globalAlpha設定

globalAlpha属性はCanvas全体の透明度を設定できます。0.0（透明）～1.0（不透明）の間で設定します。

個々の描画ではなくCanvas全体の透明度が変わる点に注意が必要です。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");
        context.globalAlpha = 0.5;

        context.fillStyle = "#0af";
        context.fillRect(0, 0, 500, 350);

        context.fillStyle = "#f0a";
        context.fillRect(50, 50, 100, 100);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/9d7ef7d2-79f6-1d2a-7a6f-6a9254682b55.png)

## globalCompositeOperation設定

globalCompositeOperation 属性はCanvasで複数の描画を行った際の既存の描画との合成方法を変更します。Photoshopなどで言えば描画モード（Blend mode）に近いイメージです。

`context.globalCompositeOperation = "対象の設定"`といったように文字列で指定します。様々な設定があるので1つ1つ後の節で触れていきます。

デフォルトでは`source-over`の設定となります（globalCompositeOperation の設定を省略した場合も`source-over`相当の挙動になります）。これは後から描画したものが上に来る形となります。

以下のコード例では最初にシアンの四角を描画し、その後に一部が重なる形でマゼンタの四角を描画しています。`source-over`としてそのまま後のマゼンタの四角が上に乗っかっていることが確認できます。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        context.fillStyle = "#0af";
        context.fillRect(0, 0, 100, 100);

        context.fillStyle = "#f0a";
        context.fillRect(50, 50, 100, 100);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/518309b9-c9d3-329a-ef8f-dd05c278f0cd.png)

以降の節での各設定は上記のコードの2つの四角をベースに進めていきます。

### source-in 設定

source-in設定は描画前のCanvas内の描画とsource-in設定後に追加する描画間で重なり合う部分のみが描画されます。重ならない部分は描画されなくなります。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        context.fillStyle = "#0af";
        context.fillRect(0, 0, 100, 100);

        context.globalCompositeOperation = "source-in";

        context.fillStyle = "#f0a";
        context.fillRect(50, 50, 100, 100);
    </script>
</html>
```

上記のコードではサイズ100のシアンとマゼンタの2つの四角を描画していますが、重なり合う真ん中の一部の部分のみ残っていることが確認できます。また、重なっている箇所は後から描画するマゼンタの四角の方が上に来るため結果的にシアンの四角はまったく描画されない状態になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7f3f5de8-93c0-f30d-b0d0-d92c8451aebd.png)

また、`context.globalCompositeOperation = "source-in";`の指定は最初のシアンの四角の描画が終わって且つマゼンタの四角を描画する前の位置で指定している点にも留意してください。シアンの四角の描画前などに設定してしまうとシアンの四角描画時にはCanvas上の描画は空になっているため描画が重なり合う部分は皆無で結果的に全て透明となり、さらにその後に続くマゼンタの四角の描画時でもCanvas上の描画は空になっているので結果的に何も描画されなくなってしまいます。

### source-out 設定

source-outはsource-inと逆の挙動をします。つまり描画前のCanvasの現在の描画と新たな描画で重なる部分が透明となり、重ならない部分のみが描画されます。また、描画前の既存の描画に関しては透明になります。

以下のコード例では中央の重なっている部分は透明となり、そして既存の描画に関しても透明になるため結果的に後から描画したマゼンタの四角の右下の方の部分のみ描画されます。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        context.fillStyle = "#0af";
        context.fillRect(0, 0, 100, 100);

        context.globalCompositeOperation = "source-out";

        context.fillStyle = "#f0a";
        context.fillRect(50, 50, 100, 100);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1f8efe1d-cc89-9447-4359-915695df0412.png)

### source-atop 設定

source-atop設定は既存の描画と設定後の新たな描画で重なる部分の描画が残ります。また、既存の元の描画部分も残ります。

以下のコード例では中央部分の一部重なる部分はマゼンタの四角が残り、そして既存の元の描画も残るためシアンの四角部分も残っています。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        context.fillStyle = "#0af";
        context.fillRect(0, 0, 100, 100);

        context.globalCompositeOperation = "source-atop";

        context.fillStyle = "#f0a";
        context.fillRect(50, 50, 100, 100);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/a0712bc4-f096-099a-a870-3695d73a9bbb.png)

### destination-over 設定

destination-over設定はsource-over設定（後から描画したものが上に来る設定）と逆の挙動をします。つまり先に描画したものが上にきます。

以下のコード例ではシアンの四角を先に描画した一方で後から描画したマゼンタの四角の上に来ていることが確認できます。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        context.fillStyle = "#0af";
        context.fillRect(0, 0, 100, 100);

        context.globalCompositeOperation = "destination-over";

        context.fillStyle = "#f0a";
        context.fillRect(50, 50, 100, 100);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e551f35b-ad42-8dfa-86be-8ad63c5ebfac.png)

### destination-in 設定

destination-in設定はsource-in設定（重なり合う部分のみを描画し、後から追加したものが上に来る）と同じように重なり合う部分のみを描画します。ただしこちらは先に追加したものが上にきます。

以下のコード例では2つの四角で重なり合う中央部分のみが残り、そして先に描画したシアンの方の四角が上に来る形になることが確認できます。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        context.fillStyle = "#0af";
        context.fillRect(0, 0, 100, 100);

        context.globalCompositeOperation = "destination-in";

        context.fillStyle = "#f0a";
        context.fillRect(50, 50, 100, 100);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/37fcffc3-3521-771c-ee11-3f6de896dc15.png)

### destination-out 設定

destination-out設定は重なり合わない部分のみ描画し、且つsource-outとは逆に先に描画した方のみが残ります。後から描画した方は残りません。

以下のコード例では先に描画したシアンの四角のみが残り、且つ重なりあっていない領域のみ残っていることを確認できます。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        context.fillStyle = "#0af";
        context.fillRect(0, 0, 100, 100);

        context.globalCompositeOperation = "destination-out";

        context.fillStyle = "#f0a";
        context.fillRect(50, 50, 100, 100);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/aa26eead-f39a-60c7-bdba-d101d436b27c.png)

### destination-atop 設定

destination-atop設定はsource-atop設定と同様に重なり合う部分が残ります。また、source-atopとは逆に重なり合う部分は先に描画した方が残され、且つ後から描画した方の重なっていない領域も残されます。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        context.fillStyle = "#0af";
        context.fillRect(0, 0, 100, 100);

        context.globalCompositeOperation = "destination-atop";

        context.fillStyle = "#f0a";
        context.fillRect(50, 50, 100, 100);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/4162a374-b8cb-a753-c91b-92abf4d5b1df.png)

### lighter 設定

lighter設定は重なり合う箇所が明るくなる形になります。描画モードの加算に近い感じでしょうか。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        context.fillStyle = "#0af";
        context.fillRect(0, 0, 100, 100);

        context.globalCompositeOperation = "lighter";

        context.fillStyle = "#f0a";
        context.fillRect(50, 50, 100, 100);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1cf2ca9a-7bab-8341-90ec-77db246ed64d.png)

### xor 設定

xor設定は重なり合う部分は透明となり、重ならない部分のみ描画が残ります。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        context.fillStyle = "#0af";
        context.fillRect(0, 0, 100, 100);

        context.globalCompositeOperation = "xor";

        context.fillStyle = "#f0a";
        context.fillRect(50, 50, 100, 100);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e8015ce4-4bfa-ca97-7f1d-b355bb593852.png)

### multiply 設定

multiply設定はいわゆる描画モードの乗算です。重なり合う部分が暗くなります。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        context.fillStyle = "#0af";
        context.fillRect(0, 0, 100, 100);

        context.globalCompositeOperation = "multiply";

        context.fillStyle = "#f0a";
        context.fillRect(50, 50, 100, 100);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/bcca778d-ca17-816f-772b-d9f7a244e98f.png)

### screen 設定

screen設定はいわゆる描画モードのスクリーンです。計算内容までは今まで把握していなかったのですが、どうやら重なり合う部分の色を反転させ、それぞれを乗算した上でさらに反転させる・・・という計算のようです。結果的に重なる部分は明るくなります。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        context.fillStyle = "#0af";
        context.fillRect(0, 0, 100, 100);

        context.globalCompositeOperation = "screen";

        context.fillStyle = "#f0a";
        context.fillRect(50, 50, 100, 100);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/a1adc629-f712-e915-7fa2-d6df7156ad84.png)

### overlay 設定

overlay設定はいわゆる描画モードのオーバーレイです。それぞれの重なり部分で先に描画されている領域が後から描画される領域の色よりも暗ければより暗く、明るければより明るくなる計算となります（結果的にコントラストの強い画像になります）。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        context.fillStyle = "#0af";
        context.fillRect(0, 0, 100, 100);

        context.globalCompositeOperation = "overlay";

        context.fillStyle = "#f0a";
        context.fillRect(50, 50, 100, 100);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/05935b06-b21b-ae3b-3ff9-4c2a23b091ad.png)

## strokeStyle設定

strokeStyle属性は線の色を設定できます。fillStyleと同様に16進数でのカラーコードやredなどの色名、rgba(...)といった指定などができます。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        context.strokeStyle = "#0af";
        context.lineWidth = 5;
        context.strokeRect(50, 50, 50, 50);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6bdc1d68-16d8-7b96-eda7-9f4a1bd45d55.png)

## lineWidth設定

lineWidt設定では線幅を設定できます。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        context.strokeStyle = "#0af";
        context.lineWidth = 10;
        context.strokeRect(50, 50, 50, 50);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/5173ba79-37f5-29fd-2063-d5d211a950ce.png)

## lineCap設定

lineCap属性は線の端に設定するスタイル設定です。文字列で指定します。SVGなどと同様に以下の設定が存在します。

- butt: デフォルトの設定です。端に対して特にスタイルを設定しません。
- round: 線の端を丸くする形のスタイル設定です。
- square: 線の端に四角を追加する形のスタイル設定です。buttと見かけ上は似ていますがbuttに対して線幅分だけ線が長くなります（round設定時と同じ長さになります）。

以下のコードではそれぞれ上からデフォルト（butt）、round、squareを設定しつつ線を描画しています。beginPathを使っていますがbeginPathは本記事では割愛します（次の記事などで触れる想定です）。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        context.strokeStyle = "#0af";
        context.lineWidth = 10;
        context.moveTo(50, 50);
        context.lineTo(150, 50);
        context.stroke();

        context.lineCap = "round";
        context.beginPath();
        context.moveTo(50, 80);
        context.lineTo(150, 80);
        context.stroke();

        context.lineCap = "square";
        context.beginPath();
        context.moveTo(50, 110);
        context.lineTo(150, 110);
        context.stroke();
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/d12550fb-a320-d495-7fd0-6bad3aaea49c.png)

## lineJoin設定

lineJoin属性は折れ線などの折れの部分（連結部分）のスタイルを設定します。文字列で以下の値を受け付けます。

- miter: デフォルト値です。折れの部分が鋭い感じになります。
- round: 折れの部分が丸くなります。
- bevel: 折れの部分が削られて斜めになります（ベベル）。

bevelとかは文字での説明よりも結果を見たほうが分かりやすそうです。以下のコードでは左の折れ線はデフォルト（miter）、真ん中にround、右にbevelを設定しています。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        context.strokeStyle = "#0af";
        context.lineWidth = 10;

        context.moveTo(50, 100);
        context.lineTo(70, 50);
        context.lineTo(90, 100);
        context.stroke();

        context.lineJoin = "round";
        context.beginPath();
        context.moveTo(150, 100);
        context.lineTo(170, 50);
        context.lineTo(190, 100);
        context.stroke();

        context.lineJoin = "bevel";
        context.beginPath();
        context.moveTo(250, 100);
        context.lineTo(270, 50);
        context.lineTo(290, 100);
        context.stroke();
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/aebd2ae8-937a-c77b-5b01-38bbf2113bd6.png)

## miterLimit設定

miterLimit設定は折れ線の折れの部分のmiter設定での上限比率が設定できるようです。小さい値などを設定した際には尖った折れの部分ではなくbevelのように削られた感じになります。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");

        context.strokeStyle = "#0af";
        context.lineWidth = 10;

        context.miterLimit = 1;
        context.moveTo(50, 100);
        context.lineTo(70, 50);
        context.lineTo(90, 100);
        context.stroke();
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/ca4ab804-1f47-dd4d-5d3c-c1b7e78f7c23.png)

# 描画の各インターフェイス

## 四角を描画するfillRectメソッド

fillRectメソッドでは四角を描画します。塗りの領域のみで境界線などは描画されません。

第一引数はX座標、第二引数はY座標、第三引数は幅、第四引数は高さの指定となります。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");
        context.fillStyle = "#0af";
        context.fillRect(50, 50, 150, 100);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/0815d433-7769-fabc-2717-ac4900c979b2.png)

## strokeRectメソッド

strokeRectメソッドでは四角の境界線部分を描画することができます。塗り部分は描画されません。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");
        context.strokeStyle = "#0af";
        context.lineWidth = 10;
        context.strokeRect(50, 50, 150, 100);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/314521e0-7cfc-a08d-37fd-1d69a1828e1d.png)

なお、他の図形などでも同じような形となりますが塗りと線両方描画したい場合にはfillRectとstrokeRectの両方のメソッドを続けて描画する形となります。

以下の例では`fillRect`と`strokeRect`メソッド両方を同じ座標とサイズで指定しているので塗りと線両方が設定されます。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");
        context.fillStyle = "#3ef";
        context.strokeStyle = "#0af";
        context.lineWidth = 10;
        context.fillRect(50, 50, 150, 100);
        context.strokeRect(50, 50, 150, 100);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c67d1cd3-9ad4-dab4-69d4-bfc705c909b3.png)

## clearRectメソッド

clearRectメソッドでは指定した四角の領域の描画を削除します。

第一引数はX座標、第二引数はY座標、第三引数は幅、第四引数は高さとなります。

以下のコード例では`context.clearRect(100, 100, 100, 50);`という指定をしており、四角の中の一部の描画が削除されます。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");
        context.fillStyle = "#0af";
        context.fillRect(50, 50, 150, 150);
        context.clearRect(100, 100, 100, 50);
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/ae22d4ad-cd7e-353b-7e8c-1f01312909a5.png)

## fillメソッド

fillメソッドは現在設定されているパス設定に対して塗りの描画を行います。パスを描画するrectやarc、lineToメソッドなどを呼び出した後に実行します。

rectやlineToメソッドなどのパス描画のメソッドは複数回呼び出しておいてからfillメソッドを1回だけ呼んでパスのセットに対して描画を行うといったことも可能です。また、rectやlineToメソッドなどはパスの設定のみで実際の塗りや線の描画は実行されません。fillや後述するstrokeメソッドを呼び出した時点で初めて描画が実行されます。

以下の例ではrectメソッドで四角のパスを設定し、fillメソッドで描画を行っています（rectメソッドに関しては後の節で詳しく触れます）。※説明のためにシンプルな記述としてありますが、これだけであればfillRectの方が同じ内容をシンプルに描画ができます。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");
        context.fillStyle = "#0af";
        context.rect(50, 50, 150, 150);
        context.fill();
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/30324428-c2c5-3c90-996f-578799014d3f.png)

## strokeメソッド

strokeメソッドはfillメソッドの線描画版のメソッドとなります。設定されているパスに対して境界線の描画を行います。複数件のパスに対して一括で描画が行える点などの挙動はfillメソッドと同じような挙動になります。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");
        context.strokeStyle = "#0af";
        context.lineWidth = 10;
        context.rect(50, 50, 150, 150);
        context.stroke();
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6c403af8-d1ab-c551-80bb-bf79a01a7cb2.png)

## rectメソッド

rectメソッドでは四角のパスを設定します。これだけだと描画はされないので別途fillやstrokeメソッドなどの描画のインターフェイスを呼び出す必要があります。

他の四角関係のインターフェイスと同様に第一引数はX座標、第二引数はY座標、第三引数は幅、第四引数は高さとなります。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");
        context.fillStyle = "#0af";
        context.rect(50, 50, 150, 150);
        context.fill();
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/430c934e-d9fc-2c5b-2bc9-3a866c707fb0.png)

## arcメソッド

arcメソッドでは円弧のパスを設定します。これだけだと描画はされないので別途fillやstrokeメソッドなどの描画のインターフェイスを呼び出す必要があります。

第一引数は円弧の中央のX座標、第二引数は円弧の中央座標のY座標、第三引数は円弧の半径、第四引数は開始角度、第五引数は終了角度、そして第六引数は省略可となりますが描画方向を半時計周りにするかどうかの真偽値となります（デフォルトでは時計周りとなります）。

第四引数と第五引数の値はラジアンの値となります。度から変換する場合には$度の値 × π / 180$といった計算が必要になります。また、開始角度は上からではなく右からスタートとなるようです。

以下の例では円弧の中央座標に100を設定し、半径に50を設定し、0度～180度に対して描画の設定をして半円を描画しています。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");
        context.fillStyle = "#0af";
        context.arc(100, 100, 50, 0, 180 * Math.PI / 180);
        context.fill();
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/a060dd97-d735-0ad1-396c-bb5e064bf773.png)

注意点として円弧の中央座標 → 開始座標 → 終了座標とパスが設定されるのではなく開始座標 → 終了座標とだけ描画されます。そのため終了座標が180度未満の場合には中央座標付近は描画されなくなります。その辺りの描画が必要な場合には別途パス設定などが必要になります。

以下の例では終了角度を90度にしているため円弧の中央付近は描画されていない状態になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e5963862-9a9a-908f-bbf0-952817e3aab0.png)

第六引数の真偽値はtrueを設定すると描画が半時計周りの方向に対して実行されます。以下の例ではtrueを指定しているため半時計周り方向になり、半円の描画方向が下ではなく上に変わっています。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");
        context.fillStyle = "#0af";
        context.arc(100, 100, 50, 0, 180 * Math.PI / 180, true);
        context.fill();
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b07646a8-9c03-a002-779f-4d1d61ca86ea.png)

## lineToメソッド

lineToメソッドは指定された座標に対して線のパスを設定します。第一引数はX座標、第二引数はY座標となります。

複数回連続して呼び出すことで折れ線グラフなどの表現ができます。また、最初のlineTo（最初のパス設定）の分は描画されないようです（最初は後述するmoveToメソッドを使う方が直観的かもしれません）。

以下の例では3回lineToを呼び出しています。最初の`context.lineTo(50, 50);`の分は描画されていません（`(0, 0)`から`(50, 50)`へは描画されていないことを確認できます）。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");
        context.strokeStyle = "#0af";
        context.lineWidth = 5
        context.lineTo(50, 50);
        context.lineTo(100, 50);
        context.lineTo(100, 100);
        context.stroke();
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e187b53c-24be-1d1f-4f81-b6e8394f20e8.png)

## moveToメソッド

moveToメソッドはパスの位置を指定された座標に移動させます。現在位置から移動先の位置へはパスは設定されず、その後に続くパス設定は新たなパスとなります。第一引数は移動先のX座標、第二引数は移動先のY座標となります。

以下の例ではlineToの間にmoveToメソッドを挟んでいるため、2つの線のパスは連結されておらず別のパスとなっています。

```html
<html>
    <body>
        <canvas id="canvas" width="500" height="350">
        </canvas>
    </body>

    <script type="text/javascript">
        let canvas = document.getElementById("canvas");
        let context = canvas.getContext("2d");
        context.strokeStyle = "#0af";
        context.lineWidth = 5
        context.moveTo(50, 50);
        context.lineTo(100, 50);
        context.lineTo(100, 100);
        context.moveTo(50, 100);
        context.lineTo(50, 150);
        context.lineTo(100, 150);
        context.stroke();
    </script>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/fed6b358-4a86-9bfe-66ad-551a15273d2f.png)

# 参考文献・参考サイト

https://www.amazon.co.jp/dp/B0085TJYKU/

https://developer.mozilla.org/ja/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes

https://developer.mozilla.org/ja/docs/Web/API/CanvasRenderingContext2D/arc

https://www.rapidtables.com/convert/number/degrees-to-radians.html

https://developer.mozilla.org/ja/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation

https://note.affi-sapo-sv.com/js-globalcompositeoperation.php

https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin