Canvas 入門

# 概要

ある案件で、スマホアプリで撮影した写真を画像処理する事になり、内容的には単純な動作のアプリなので、ハイブリットアプリを採用することにしました。
ベースはReact + Cordovaで制作する事に決定。
画像処理にはCanvasを利用しようと思ったのですが、勉強したのが2年前くらいだったので、再度お勉強する事にしました。

# Canvasとは

JavaScriptとHTML5を組み合わせて画像を描画する要素です。
簡単なアニメーションなども制作可能です。

# 必要知識

- HTML
- CSS
- JavaScript

# 前提条件

- canvas要素内で生成されたオブジェクト(.getContext()により生成されたオブジェクト)はctxという名称にする。

# 基本知識

## canvas要素

canvasはcanvas要素内に描画される。

```html:index.html
      <div>
        <h1>Canvasの練習</h1>
        <canvas id="myCanvas" width="400" height="200">
          Canvasに対応したブラウザを使ってください。
        </canvas>
      </div>
```

canvas要素に対応していないブラウザでは、cnavas要素に囲まれた文字が表示される事になります。
JavaScriptで後で使いやすい様に予めidを与えています。

## canvas内に四角を描画する。

canvas内にJavaScriptで四角を描画します。

```js:canvas.js
let draw = () => {
  let canvas = document.getElementById('myCanvas');
  if (!canvas || !canvas.getContext) {
    return;
  }
  let ctx = canvas.getContext('2d');
  ctx.strokeRect(10, 10, 50, 50);
};

window.onload = () => {
  draw();
};

```

単純な四角を描画するだけなら、以上で描画できます。

やっている事は単純で、

1. canvas要素の取得。
2. canvas要素の存在チェック。
3. canvas要素のgetContextメソッドでContextを指定し、ctxに代入。
4. 変数ctxのメソッドを利用して、四角を描画する。

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

#### 描画メソッド

##### ctx.strokeReact(X, Y, W, H)

線で四角を描画します。
X, Yに関しては描画開始位置を指定できます。
(本例では、widht=400, height=200の要素の中から、左上を開始点としています。)

##### ctx.fillRect(X, Y, W, H)

塗りつぶしの四角を描画します。

##### ctx.clearReact(X, Y, W, H)

四角でくり抜きます。

#### スタイルプロパティ

##### ctx.strokeStyle

線の色を指定します。

##### ctx.fillStyle

塗りの色を指定します。

##### ctx.lineWidth

線の幅を指定します。

##### ctx.lineJoin

線の角を指定します。
代入する値は、'round'などを指定します。

#### ctx.lineCap

線の端の描画を変更する。
roundなどを指定すると線の端が丸みをおびます。

#### グラデーション

塗りのオブジェクトにグラデーションを追加する事も可能です。

グラデーションを指定するには、まずグラデーション用のオブジェクトを用意します。

##### ctx.createLinearGradient(X, Y, X, Y)

線形グラデーションのスタイル用オブジェクトを生成します。
最初のXYには、グラデーションの開始位置、次のXYにはグラデーションの終了位置を指定します。

##### ctx.createRadialGradient(X, Y, R, X, Y, R)

円形グラデーションのスタイル用オブジェクトを生成します。
最初のXYには、グラデーションの開始位置、次のXYにはグラデーションの終了位置を指定します。
Rには円の半径を指定します。
###### ctx.addColorStop(0.0, [color])

グラデーションのどの位置でどの色を発色するかを指定します。

例えば、線形グラデーションの指定は以下の様になります。

```js:canvas.js
  let canvas = document.getElementById('myCanvas');
  if (!canvas || !canvas.getContext) {
    return;
  }
  let ctx = canvas.getContext('2d');
  let g = ctx.createLinearGradient(0, 0, 100, 100);
  g.addColorStop(0.0, 'red');
  g.addColorStop(0.5, 'blue');
  g.addColorStop(1.0, 'yellow');
  ctx.fillStyle = g;
  ctx.fillRect(10, 10, 100, 100);
```

![スクリーンショット 2016-09-25 10.47.05.png](https://qiita-image-store.s3.amazonaws.com/0/129216/3f7a69f6-ce58-97a4-304d-6ccfff3f66a6.png)


#### 影を付ける

##### ctx.shadowColor

影の色を指定します。

##### ctx.shadowOffsetX

影のX地点を指定します。

##### ctx.shadowOffsetY

影のY地点を指定します。

##### ctx.shadowBlur

影のぼかし具合を指定します。

#### 透明度を指定する。

##### ctx.globalAlpha

0.0 ~ 1.0で透明度を指定します。

### canvas内のオブジェクトの変形

オブジェクトを変形させる為には、オブジェクト生成前に変形具合を指定する必要があります。

#### ctx.scale(X.X, Y.Y)

オブジェクトを拡大・縮小さます。
指定のXYは0.8などの割合を指定します。

#### ctx.rotate(R)

オブジェクトを回転させます。
度で回転させたい場合は、決まり文句として下記を指定します。

```js:canvas.js

  ctx.rotate(30/180*Math.PI);
```

回転させたい度数(ここでは30度)に180*円周率の値を割ってあげます。

#### ctx.translate(X, Y)

オブジェクトを移動させます。
アニメーションなどでも利用するメソッドになります。

## 線を描く

Canvasでは線を描いたり、曲線を描く事も可能です。

線を描き始めるにはまず、線を描くという事を明示的に宣言する必要があります。イメージとしては、筆を準備するイメージです。

### ctx.beginPath()

線を描く事を宣言します。

### ctx.moveTo(X, Y)

線の開始位置をしていします。

### ctx.lineTo(X, Y)

線の終着点を指定します。
moveToメソッドで指定した箇所からどこに向かって線を引くか指定するイメージです。

### ctx.closePath()

終了点という事を明示的に宣言し、開始点に向けて線を引きます。

### ctx.stroke()

strokeメソッドを実行されてはじめて線が描画されます。

### ctx.fill()

線で書いた図形を塗りつぶしで描画します。

### ctx.arc(X, Y, R, F1, F2)

曲線を描画します。
Xには中心点のX座標、Yには中心点のY座標、Rには描画する円の半径を指定します。

F1には中心点から見ての開始度数点を指定し、F2には中心点から見ての終了度数点を指定します。

正円を書くには以下の様にします。

```js:canvas.js

  ctx.beginPath();
  ctx.arc(100, 100, 50, 0/180*Math.PI, 360/180*Math.PI);
  ctx.stroke();
```

![スクリーンショット 2016-09-25 10.47.33.png](https://qiita-image-store.s3.amazonaws.com/0/129216/b266c069-1d7c-7fb9-9e49-30704d05abff.png)


## テキストの描画

グラフィックなど描画した後に、ラベル的に文字を記載したい事などがあると思います。そういう時に利用できるメソッドです。

### ctx.font

CSSで指定する様な値を指定できます。

### ctx.textAlign

文字揃えを指定できます。

### ctx.fillText(S, X, Y, [W])

文字を描画します。
Sには描画したい文字を指定します。
Wには最大の幅を指定します。

### ctx.strokeText(S, X, Y, [W])

縁取りした文字を描画します。

以下の様になります。

```js:canvas.js
  ctx.font = 'bold 20px Verdana';
  ctx.fillStyle = 'red';
  ctx.textAlign = 'left';

  ctx.fillText('Gemcook', 20, 20);
```

![スクリーンショット 2016-09-25 10.55.03.png](https://qiita-image-store.s3.amazonaws.com/0/129216/f9059542-b9f1-5480-a8f7-9a9234456171.png)


## 画像を表示

JPEGやPNGなどの画像を配置する方法です。
若干くせがあります。

① imageオブジェクトを生成する。

```js:canvas.js
  let img = new Image();
```

まずはimageオブジェクトをインスタンス化します。

② imageオブジェクトに画像のパスを代入します。

```js:canvas.js
  img.src = './img/images.png';
```

③ 画像の読み込み処理が完了した後に実際に画像をcanvas要素に描画します。

```js:canvas.js
  img.onload = () => {
    ctx.drawImage(img, 10, 10);
  };

```

### ctx.drawImage(I, X, Y)

画像を描画します。

最終的には以下のソースになります。

```js:canvas.js
  let img = new Image();
  img.src = './img/images.png';
  img.onload = () => {
    ctx.drawImage(img, 10, 10);
  };
```

![スクリーンショット 2016-09-25 11.13.22.png](https://qiita-image-store.s3.amazonaws.com/0/129216/bfe2bf67-7e8f-c4bf-0e52-6665a6cfa717.png)


## 設定の保存・復元

canvasの描画設定を保存・復元する事も可能です。

```js:canvas.js
  ctx.fillStyle = 'yellow';
  ctx.fillRect(10, 0, 50, 50);
  ctx.save();

  ctx.fillStyle = 'blue';
  ctx.fillRect(110, 0, 50, 50);

  ctx.restore();
  ctx.fillRect(210, 0, 50, 50);
```

![スクリーンショット 2016-09-25 11.23.53.png](https://qiita-image-store.s3.amazonaws.com/0/129216/29dc3206-8f29-a2cf-c283-4f31ea2343e8.png)


上記の例では、まず黄色の四角を描画し設定を保存しています。
その後青の四角を描画しています。
最後の黄色はsave()している設定をrestoreして再び黄色で四角を描画しています。
単純な例ですが、複雑な設定をしていて、元に戻したい時に便利です。


### ctx.store()

現在までのcanvasオブジェクトへの指定を保存します。

### ctx.restore()

現在保存している設定をcanvasオブジェクトに保存します。

## おしゃれな背景画像を作ってみる

canvasの応用編としておしゃれな画像を作ってみたいと思います。

以下になります。

```js:canvas.js
let draw = () => {
  let canvas = document.getElementById('myCanvas');
  if (!canvas || !canvas.getContext) {
    return;
  }
  let ctx = canvas.getContext('2d');

  ctx.globalAlpha = 0.5;

  let roundColor = () => {
    return Math.floor(Math.random() * 255);
  };

  for (let i = 0; i < 100; i++) {
    let x = Math.floor(Math.random() * 400);
    let y = Math.floor(Math.random() * 200);
    let r = Math.floor(Math.random() * 200);

    ctx.fillStyle = `rgb(${roundColor()}, ${roundColor()}, ${roundColor()})`;
    ctx.beginPath();
    ctx.arc(x, y, r, 0, 2*Math.PI);
    ctx.stroke();
    ctx.fill();
  }
};
```

![スクリーンショット 2016-09-25 11.43.59.png](https://qiita-image-store.s3.amazonaws.com/0/129216/da945ae8-8c46-9bc7-c85e-49744e6842d3.png)


ランダム数値で色と配置場所を指定しているので、リロードする度に画像が変化します。
試して見て下さい。

## canvasアニメーション

JavaScriptっぽいやり方でcanvasでアニメーションを作成する事も可能です。

肝は、ループを回す事、ループ内で1回1回canvasをクリアしてあげることです。

以下になります。

```js:canvas.js
let draw = () => {
  let canvas = document.getElementById('myCanvas');
  if (!canvas || !canvas.getContext) {
    return;
  }
  let ctx = canvas.getContext('2d');

  ctx.fillStyle = 'red';
  let y = 0;

  let loop = () => {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    if (y > canvas.height) y = -50;
    y++;
    ctx.fillRect(10, y, 50, 50);
    setTimeout(loop, 10);
  }
  loop();
}

window.onload = () => {
  draw();
};

```

![push.gif](https://qiita-image-store.s3.amazonaws.com/0/129216/44b7e19d-41a4-186f-bb82-c9effd89e85d.gif)


最初にyを初期化してあげます。
その後に関数を準備して、再帰的に関数を実行します。
canvas.widthとcanvas.heightでcanvas要素自体のMax値を取得できます。
canvasは一回描画したオブジェクトは残り続けますので、必ずclearRectしてから描画する様にして下さい。

# 最後に

だいたい今時のアニメーションはCSSだけでやってしまうのが主流(僕もそうです。)が、ハイブリットアプリでの画像処理などの際にはどうしてもJSの力が必要になるかと思います。

ちょっと困った時の知識として身につけておけたらと思います。