LoginSignup
3
3

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-09-16

やりたいこと

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

準備

Jimpをインストール

npm install --save jimp

ReferenceError対応

もしReferenceError: regeneratorRuntime is not definedというエラーが出た場合はこちらご参考ください

実装

UIとかCSSはこちらの記事参考にしてます!
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を入れて呼び出しました。


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

Jimpに渡すデータの指定方法

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

多謝

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3