スマフォのカメラロールの画像を使う


## やること

1. カメラロールで画像を選択する
2. ブラウザ上に表示する
3. canvasで画像に効果をつける
4. おまけ

## 準備

html,js,cssを準備しておく。
今回は↓のような構成。

```javascript:構成
Camera−Roll
 ┃
 ┣━ index.html
 ┃
 ┣━ js
 ┃   ┃
 ┃   ┗━ script.js
 ┃
 ┗━ css
      ┃
      ┗━ style.css
    
```


## カメラロールで画像を選択する

画像を選択させるために`input`要素をおく。
`type="file"`にしておくと、タップしたときに写真を撮るかカメラロールから選ぶかを選択するおなじみのアレが出てくる。
`id`属性はあとでjs側でつかうのでつけておく

```html:index.html
<input type="file" id="file" />
```

<img src="https://qiita-image-store.s3.amazonaws.com/0/53664/4e6d5284-f1c2-8c08-e4e1-c4e4a8a74b47.png" alt="おなじみのアレ" width="250" />

## ブラウザ上に表示する

今のままではファイルを選択しても特に何も起きないので、選択したファイルをあれこれする処理をjsに書いていく。

jsでの主な処理は以下のとおり。

1. ファイルが選択されたイベントをキャッチする
2. `FileReader`を使い、選択されたファイルをURL形式で読み込む
3. 取得したURL形式のデータを、Imageオブジェクトのsrcに指定する

ではやっていきます。

#### ファイルが選択されたイベントをキャッチする

```javascript:script.js
// 要素を取得
var input = document.getElementById('file');
// changeイベントでファイルの選択をキャッチ
input.addEventListener('change', function (e) {
  // 処理
  alert('ファイルが選択されました');
});
```

ここまででこんな感じ。

<img src="https://qiita-image-store.s3.amazonaws.com/0/53664/936acac7-bb5a-9cc5-7290-866cb5c357b5.png" alt="イベントをキャッチ" width="250" />

#### FileReaderを使い、選択されたファイルをURL形式で読み込む

[FileReader](https://developer.mozilla.org/ja/docs/Web/API/FileReader)を使うことで、選択されたファイルをいろんな形式で扱えるようになる。
今回はURL形式で扱う。

```javascript:script.js
var input = document.getElementById('file');
input.addEventListener('change', function (e) {
  // コールバック引数のsrcElementに、filesという名前で配列形式でファイルオブジェクトが格納されている
  // 今回は単一ファイルしか選択させないので、0番目に選択したファイルがある
  var file = e.srcElement.files[0];

  // FileReaderを初期化して、
  var fr = new FileReader();
  // ファイルが読み込み終わったあとの処理を書いておく
  fr.addEventListener('load', function() {
    // resultプロパティに読み込んだデータが入ってくる
    var url = fr.result;
    alert(url);
  });
  // 最後にreadAsDataURLメソッドに選択したファイルオブジェクトを渡すと
  // 上記resultプロパティにURL形式でデータが入る
  fr.readAsDataURL(file);
});
```

ここまででこんな感じ。
<img src="https://qiita-image-store.s3.amazonaws.com/0/53664/a717bbe8-9b43-cdd6-c6cc-7daa7b09d467.png" alt="URL形式のデータ" width="250" />


#### 取得したURL形式のデータを、Imageオブジェクトのsrcに指定する

前のステップまでで選択したファイルをURL形式のデータで扱うことができるようになっているので、あとはそんなに難しくない。

```javascript:script.js
var input = document.getElementById('file');
input.addEventListener('change', function (e) {
  var file = e.srcElement.files[0];

  var fr = new FileReader();
  fr.addEventListener('load', function() {
    var url = fr.result;
    
    // Imageオブジェクトをつくり
    var img = new Image();
    // srcにurlを指定する
    img.src = url;
    // そのままだとでかすぎるのでサイズ調整して
    img.height = 200;
    // bodyに追加する
    document.body.appendChild(img);
  });
  fr.readAsDataURL(file);
});
```

するとこんな感じ。

<img src="https://qiita-image-store.s3.amazonaws.com/0/53664/542d8ed2-a73d-f985-085a-3981f0429857.png" alt="Image" width="250" />

これまでずっとカメラロールからファイルを選択するという話をしてきたが、その場で写真を撮ってもいける。

シャッターを押すと↓みたいになるので、
<img src="https://qiita-image-store.s3.amazonaws.com/0/53664/c9ad0f59-01b2-edb8-26d0-af5e2e78e816.png" alt="シャッターを押したあと" width="250" />

`Use Photo`を押すとカメラロールのときと同じように表示される。

<img src="https://qiita-image-store.s3.amazonaws.com/0/53664/95cc3642-6521-cae8-da94-ebc6fae9337f.png" alt="カメラロールと同じように表示される" width="250" />

## canvasで画像に効果をつける

Imageオブジェクトで扱えるようになっているので、canvasに描き出すことも簡単。
まずは普通にcanvasに描きだしてみる。

```javascript:script.js
var input = document.getElementById('file');
input.addEventListener('change', function (e) {
  var file = e.srcElement.files[0];

  var fr = new FileReader();
  fr.addEventListener('load', function() {
    var url = fr.result;
    
    var img = new Image();
    // onloadでcanvasに描き込む処理を書く
    img.onload = function () {
      // canvasをつくる
      var canvas = document.createElement('canvas');
      // canvasのwidthとheightは、imgと同じ値にする
      canvas.width = img.width;
      canvas.height = img.height;

      // 見た目上のサイズは小さめにしておく
      // heightはとりあえず200
      canvas.style.height = '200px';
      // widthはimgの比率を維持するように計算
      canvas.style.width = (img.width / img.height) * 200 + 'px';

      // canvasをbodyに追加する
      document.body.appendChild(canvas);

      // contextを取得
      var context = canvas.getContext('2d');
      // imgを描き込む
      context.drawImage(img, 0, 0, canvas.width, canvas.height);
    };
    img.src = url;
  });
  fr.readAsDataURL(file);
});
```

ここまででさっきとほぼ一緒になる。
<img src="https://qiita-image-store.s3.amazonaws.com/0/53664/027c6183-fe33-5052-536c-7cdb4fe6938d.png" alt="canvasで書きだす" width="250" />

次になにか効果をつける。
いいのが思いつかなかったので、[canvasで輪郭を検出してアートっぽくする](http://qiita.com/keiskimu/items/24456deeb6ef7665a6c1)でやった補色を使う。
描き換えの詳細もそちらを参考に。

```javascript:script.js
var input = document.getElementById('file');
input.addEventListener('change', function (e) {
  var file = e.srcElement.files[0];

  var fr = new FileReader();
  fr.addEventListener('load', function() {
    var url = fr.result;
    
    var img = new Image();
    img.onload = function () {
      var canvas = document.createElement('canvas');
      canvas.width = img.width;
      canvas.height = img.height;

      canvas.style.height = '200px';
      canvas.style.width = (img.width / img.height) * 200 + 'px';

      document.body.appendChild(canvas);

      var context = canvas.getContext('2d');
      context.drawImage(img, 0, 0, canvas.width, canvas.height);

      // 補色を計算する関数
      var getComplementaryRGB = function (rgb) {
        var max = Math.max(rgb.r, rgb.g, rgb.b),
            min = Math.min(rgb.r, rgb.g, rgb.b),
            sum = max + min;
        return {
            r: sum - rgb.r,
            g: sum - rgb.g,
            b: sum - rgb.b
        };
      };

      // 描き込まれたデータを取得する
      var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
      var data = imageData.data;

      // 全ピクセル描き変える
      for (var i = 0, l = data.length; i < l; i += 4) {
        // rgbの値を取り出して
        var rgb = {
          r: data[i],
          g: data[i + 1],
          b: data[i + 2]
        };
        // 補色のrgbを計算する
        var complementaryRGB = getComplementaryRGB(rgb);
        // 計算したrgbで描き変える
        data[i] = complementaryRGB.r;
        data[i + 1] = complementaryRGB.g;
        data[i + 2] = complementaryRGB.b;
      }

      // 描き換えたdataをimageDataにもどし、描画する
      imageData.data = data;
      context.putImageData(imageData, 0, 0);
    };
    img.src = url;
  });
  fr.readAsDataURL(file);
});
```

するとこうなる。

<img src="https://qiita-image-store.s3.amazonaws.com/0/53664/d3e8eb60-4c85-6888-ac57-7983b59ae165.png" alt="補色で描き変える" width="250" />

ピッコロさんかな？

## おまけ
ファイル選択のUIがダサいのでなんとかする。
カメラロールのアレを表示させるには`Choose File`がタップ（クリック）されればいいだけなので、別のボタンを用意してそれがクリックされたらinputの`click`メソッドを呼べばおｋ。

htmlに要素を追加する。
あとでどうとでもできるように、今回はとりあえずanchorタグを追加。

```html:index.html
<input type="file" id="file" />
<a href="javascript:void(0);" id="button">Tap me!</a>
```

ダサいやつはcssで見えなくしておく。

```css:style.css
#file {
  display: none;
}
```

jsにclick時の処理を追加。

```javascript:script.js
// 略

// 要素を取得
var button = document.getElementById('button');
// clickされたら
button.addEventListener('click', function () {
  // inputのclickメソッドを呼ぶ
  input.click();
});
```

するとあのダサいやつを見なくてもよくなる。

<img src="https://qiita-image-store.s3.amazonaws.com/0/53664/52ebe921-1ca5-c7de-5c1e-ac7bdb908739.png" alt="補色で描き変える" width="250" />

## demo
![demo](https://qiita-image-store.s3.amazonaws.com/0/53664/edb412b4-1b42-5cea-8abc-b78a9ed3f558.png)