Canvasで実装するシンプルなローディング画面3パターン

# はじめに
読み込みに時間が掛かるようなコンテンツの場合、エラーでは無く読み込み中ということをユーザーに伝えるため、ローディング画面（スプラッシュ画面）を用意することはよくあるかと思います。

gifアニメーションで意図を伝えるのも良いと思いますが、できることならその読み込み状況を合わせて表示させると、よりフレンドリーかと思います。

今回はCanvasを使用して、シンプルな__3種類のローディング画面__を作成してみました。3種類とは単に思いついただけのものですが、基本的にはこれらの応用で色々なローディング画面を作成できるかと思います。

> 実際の動きは↓ページで確認ができます
> http://nekoneko-wanwan.github.io/demo/canvas/loading/


# 使用したライブラリ
- [preloadJS](http://createjs.com/PreloadJS)

> CreateJSのモジュールの一つで、色々なファイルの読み込み率が取得可能
> ※なおここでは使用していませんが、少なくとも画像だけであれば[imagesLoaded](http://imagesloaded.desandro.com/)ライブラリを使っても同じようなことはできるかと思います。


# 処理の手順
- 読み込みとしてカウントしたい画像など、外部ファイル群を定義・取得する

>・単純にソースコード内に列挙
>・JSONファイル化
>・documentを走査して自動的に生成する...など

- 描画関数を定義
- preloadJSのprogressイベントを設定（ファイルが読み込まれる度に発動）
- progressイベント内で読み込み率を渡して実行させる描画関数を記述
- preloadJSの実行

## サンプルコード
```sample.js
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');

/* 読み込みファイルの定義 */
var images = [
  {src: 'img/ph01.jpg'},
  {src: 'img/ph02.jpg'}
];

/**
 * 描画関数の定義
 * @param zero_point 0.01 ~ 1.00の値を渡す
 */
function draw(zero_point) {
  var percentage = (zero_point * 100).toFixed(0);  // 0 ~ 100%に変換
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // zero_point や percentageを使って描画する
  // ...
}

/* preloadJSの定義 */
var preload = new createjs.LoadQueue();

/* 同時読み込み数の設定（デフォルトは1） */
preload.setMaxConnections(1);

/* progressイベント */
preload.on("progress", function(e) {
  var zero_point = (e.progress).toFixed(2); // 小数点第二位まで 0.01 ~ 1.00
  draw(zero_point); // 描画関数を実行
  if (e.progress === 1) {} // 完了したら特定の処理を行うことも
});

/* 定義した外部ファイル群を読み込む。falseで実行を保留できる */
preload.loadManifest(images, false);

/* 保留にした場合の実行トリガー */
preload.load();
```


# 3種類のローディング画面

## 矩形アニメーション

- 棒グラフのように読み込み率に合わせて矩形をぐぐーっと伸ばす
- 矩形サイズをCanvas画面全体に合わせることでインパクトのある表現も
- 矩形ではなく画像を用いることでリッチな表現も

![loading-kukei.gif](https://qiita-image-store.s3.amazonaws.com/0/58436/85b677c3-aff1-11a1-9a32-dc61ae56c4cf.gif)

### 実装の考え方
- A: 横・縦のサイズを読み込み率に応じて伸縮させる
- B: 初めから横・縦の最大サイズを確保し、Canvasの領域外に配置する。読み込み率に応じてCanvas内にスライドしてくる

```rect.js
function draw() {
  var percentage = (zero_point * 100).toFixed(0);
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // メインの矩形描画
  ctx.beginPath();
  ctx.fillStyle = "#000000";
  // A
  ctx.fillRect(0, 0, percentage, 7);
  // B
  ctx.fillRect(0, canvas.height - (canvas.height * zero_point), canvas.width, canvas.height);

  // テキスト
  ctx.font = "18px 'MS Pゴシック'";
  ctx.fillStyle = "#ffffff";
  ctx.fillText(percentage + '%', 0, 40);
}
```


## 円形アニメーション

- 円を描くようにくるっと一周させる
- 円グラフを描くように塗りつぶす面積をどんどん増やす

![loading-circle.gif](https://qiita-image-store.s3.amazonaws.com/0/58436/779efbcf-76fb-e16e-eb5e-3526f2d5fb7d.gif)

### 実装の考え方
- context.arc()のstartAngle, endAngleに対し小数点を掛けあわせていく（分かりにくい...）
- 円グラフ風にする場合は、context.moveTo()で開始点を指定する必要がある

```circle.js
function draw() {
  var percentage = (zero_point * 100).toFixed(0);
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // 円周を描く
  ctx.beginPath();
  ctx.lineWidth = 5;
  ctx.arc(70, 70, 60, Math.PI * 1.5, Math.PI * 2 * zero_point + Math.PI * 1.5, false);
  ctx.stroke();

  // 円グラフを描く
  ctx.beginPath();
  ctx.moveTo(70, 70);
  ctx.lineWidth = 5;
  ctx.arc(70, 70, 60, Math.PI * 1.5, Math.PI * 2 * zero_point + Math.PI * 1.5, false);
  ctx.fill();
}
```


## マスクアニメーション

- 任意の形を用意し、そのシェイプで切り抜くことでインパクトのある表現が可能（コンセプトやキャラクターのシェイプを用いることでよりコンテンツの雰囲気が出せる）
- 複雑なシェイプを用意するのが大変なのはまた別問題...

> 切り抜く形までもアニメーションをさせたら、描画残像が汚く残ってしまいました...(chrome)

![loading-mask.gif](https://qiita-image-store.s3.amazonaws.com/0/58436/43065f50-e698-40f9-067a-6740044f0dbc.gif)


### 実装の考え方
- 切り抜きたい形を用意し、clip()を実行した後に、アニメーションさせたい描画を定義

```clip.js
function draw() {
  var percentage = (zero_point * 100).toFixed(0);
  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // 切り抜く形を定義
  ctx.beginPath();
  ctx.arc(70, 70, 60, 0, Math.PI * 2, false);
  ctx.stroke();

  // クリップ実行
  ctx.clip();

  // ただの矩形だがクリッピングされて円形となる
  ctx.beginPath();
  ctx.fillStyle = "#000000";
  ctx.fillRect(0, canvas.height - (canvas.height * zero_point), canvas.width, canvas.height);
}
```


# おわりに
ローディング用のアニメーションとして動きを作成してみましたが、簡単なインフォグラフィックにも使えそうです。


# 今まで投稿したCanvas関連の記事
- [Canvasアニメーションの要点](http://qiita.com/nekoneko-wanwan/items/33afa5d20264c83b2bd1)
- [Canvasイベント操作まとめ](http://qiita.com/nekoneko-wanwan/items/9af7fb34d0fb7f9fc870)
- [Canvasで円形(ほぼ)をマウスオーバーでぐにゃらせてみる](http://qiita.com/nekoneko-wanwan/items/2e08e4cd43b8243ce5d0)