Vue.jsでアップロードした画像を加工してプレビューしたりBlobにする

### やりたいこと
Vueでアップした画像を加工したりBlobにしたりしてプレビューも見せたい。
画像処理にはJimpを利用しました。
Blobにするのはフォームから送信するのが前提だったから。

### 準備
#### [Jimp](https://www.npmjs.com/package/jimp)をインストール

```bash
npm install --save jimp
```

#### ReferenceError対応
もし`ReferenceError: regeneratorRuntime is not defined`というエラーが出た場合は[こちら](https://qiita.com/mimaun/items/e2c79250b4f031d316a0)ご参考ください

### 実装

UIとかCSSは[こちらの記事](https://qiita.com/HikaMashiro/items/655729bc1893392dfd42)参考にしてます！
CSS等々勉強中なので命名とか書き方は見逃してください。

```uploadImage.vue
<template>
...
      <form @submit="checkForm">
      ...
        <div class="form-item">
          <label for="uploadedImage" class="form-item__label"
            >アップ画像</label
          >
          <label v-show="!previewUploadedSrc" class="img-title-label"
            >画像を選択
            <input
              ref="imageUploader"
              name="uploadedImage"
              type="file"
              @change="onFileChange"
            />
          </label>
          <div v-show="previewUploadedSrc" class="preview-item">
            <img :src="previewUploadedSrc" />
            <div class="preview-close-button" @click="onClickRemoveImage">
              <label class="preview-image__title">{{ imageTitle }}</label>
            </div>
          </div>
        </div>
        ...  
      </form>
      ...
</template>

<style scoped>
.img-title-label > input {
  display: none;
}

.img-title-label {
  padding: 0.1rem 1rem;
  border: 1px solid #888;
}

.img-title-label::after {
  content: '+';
  font-size: 1rem;
  color: #888;
  padding-left: 1rem;
}

.preview-item > img {
  width: 150px;
  height: 150px;
  background-color: #666;
  object-fit: contain;
}

.preview-image__title {
  padding: 0.1rem 1rem;
  border: 1px solid #888;
}

.preview-image__title::after {
  content: 'x';
  font-size: 1rem;
  color: #888;
  padding-left: 0.5rem;
}
</style>

<script>
...
  data() {
    return {
      previewUploadedSrc: '',
      imageTitle: '',
      uploadedImageBlob = ''
    }
  },
  methods: {
    /**
     * =================
     * 画像アップロード
     * =================
     */
    onFileChange(e) {
      let files = e.target.files || e.dataTransfer.files
      let file = files[0]
      let reader = new FileReader()
      reader.onload = e => {
        this.createImage(e.target.result)
        this.imageTitle = file.name
      }
      reader.readAsDataURL(file)
    },
    createImage(file) {
      // コールバック内からthisを呼び出すため一時的に代入
      var tmpThis = this
      // 画像サイズ調整
      Jimp.read(file, function(err, image) {
        const width = 200
        const height = 200
        image
          // 画像を指定された幅と高さに全て覆われるよう縦横比を維持して拡大/縮小します
          .cover(width, height)
          // ポスタライズとか
          .posterize(10)
          .getBase64(Jimp.MIME_PNG, function(err, src) {
            tmpThis.uploadedImageBlob = tmpThis.base64ToBlob(src)
            tmpThis.previewUploadedSrc = src
          })
      })
    },
    onClickRemoveImage() {
      this.$refs.imageUploader.value = ''
      this.uploadedImageBlob = ''
      this.previewUploadedSrc = ''
      this.imageTitle = ''
    },
    base64ToBlob(base64) {
      const bin = atob(base64.replace(/^.*,/, ''))
      const buffer = new Uint8Array(bin.length)
      for (let i = 0; i < bin.length; i++) {
        buffer[i] = bin.charCodeAt(i)
      }
      return new Blob([buffer.buffer], {
        type: 'image/png'
      })
    },
    ...
}
</script>
```

### 詰まったところ
#### Jimpで加工したものを別のメソッドに渡したいがコールバック関数内なので`this`のスコープが違う
ドキュメントチラ見していたときには他人事の顔でスルーしてましたがついにぶち当たりました。
あらかじめ`tmpThis`という別の変数に`this`を入れて呼び出しました。

```javascript

// コールバック内からthisを呼び出すため一時的に代入
var tmpThis = this
```

#### Jimpに渡すデータの指定方法
他の記事見てると大体画像がパス指定だったので、inputで取得した画像を渡すにはどうしたらいいのかなと少し悩みました。
FileReaderのonload処理の中で呼んであげれば良いだけでした。

## 多謝
- [Jimp](https://github.com/oliver-moran/jimp)
- [Vue.jsでファイルアップロードの実装--ついでにプレビュー機能も実装した話--](https://qiita.com/HikaMashiro/items/655729bc1893392dfd42)
- [画像をリサイズしてblobでプレビュー表示する方法【Vue/Canvas】](https://qiita.com/kawa64372358/items/492c89db7f96823a26c0)
- [JavaScriptで画像処理するなら「Jimp」が便利
](https://s8a.jp/javascript-image-processing-library-jimp)
- [書き方で変わる?! Vue.jsでthisのスコープを調査した](https://tadaken3.hatenablog.jp/entry/vue-scope-this)