0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【JavaScript】フォーム上などの画像ファイルをブラウザだけでリサイズ

Posted at

JavaScriptで画像ファイルをリサイズしたい時があると思います。僕はありました。
単にFileを投入して変換するコードが意外となかったのでここに共有します。
この例では、リサイズ後の画像をブラウザからダウンロードの形で取得できるようにしました。

DEMO

See the Pen GRJRrPW by hiroism (@jukaism) on CodePen.

Canvasを利用します

Canvasは図を表示するための仕様ですが、機能が豊富なので画像加工にも利用できます。

  <body>
    <canvas style="width: 484px; height: 253px;" id="canvas"></canvas>
  </body>

画像をリサイズしたい幅と高さを指定しておきます。邪魔ならdisplay: none;も可。

コード全体

htmlファイルにしてブラウザで開けば動くコードです。
スクリプト内の**resize(file)**にFileを投入するほか、
添付フィールドを用意したので、ここから画像を選んでも動作します。
image.png

<html lang="ja">
  <body>
    <input type="file" id="image_zone">
    <br>
    <canvas style="width: 484px; height: 253px;" id="canvas"></canvas>
    <script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js"></script> <!-- IEで必要。不要なら削除を -->
    <script>
      // image_zoneに新しいファイルがセットされた時、resizeを呼ぶ
      const imageZone = document.getElementById('image_zone')
      imageZone.addEventListener('change', resizePinnedImage, false)
      function resizePinnedImage(e) {
          const file = e.target.files[0]
          if (!file.type.match('image.*')) { return }
          resize(file)
      }
      // Canvasに画像をリサイズして貼り、終わったらブラウザからダウンロードっぽく取得
      function resize(file) {
        imageToCanvas(file).then(function (canvas) {
          // [5] キャンバスから画像を取得
          if (canvas.msToBlob) {
            window.navigator.msSaveBlob(canvas.msToBlob(), 'resized.png')
          } else {
            const a = document.createElement('a')
            a.href = canvas.toDataURL(file.type)
            a.download = 'resized.' + file.type.split('/')[1]
            a.click()
          }
        }).catch(function (error) {
          console.error(error)
        })
      }

      function imageToCanvas (imageFile) {
        // [1] Fileから画像データを得る
        return new Promise(function (resolve, reject) {
          readImage(imageFile).then(function (src) {
            loadImage(src).then(function (image) {
              // [2] Canvasの呼び出し
              const canvas = document.getElementById("canvas")
              const ctx = canvas.getContext('2d')
              // [3] 縮尺を計算
              const scale = Math.max((canvas.height / image.height), (canvas.width / image.width))
              // [4] 画像を切り取り、Canvasに展開して返す
              ctx.drawImage(
                // Canvasに貼り付ける画像
                image, 
                // 画像の切り取り開始位置(左上からの横距離、縦距離)
                (image.width - (canvas.width / scale)) / 2, (image.height - (canvas.height / scale)) / 2,
                // 画像の切り取り範囲(幅、高さ)
                canvas.width / scale, canvas.height / scale,
                // キャンバスの貼り付け開始位置(左上からの横距離、縦距離)
                0, 0,
                // 画像への貼り付け範囲(幅、高さ)
                canvas.width, canvas.height
              )
              resolve(canvas)
            }).catch(function (error) {
              reject(error)
            })
          }).catch(function (error) {
            reject(error)
          })
        })
      }

      // [1-A] Fileの中身を得る
      function readImage(image) {
        return new Promise(function (resolve, reject) {
          const reader = new FileReader()
          reader.onload = function () { resolve(reader.result) }
          reader.onerror = function (e) { reject(e) }
          reader.readAsDataURL(image)
        })
      }
      // [1-B] 空の画像データを作った後に中身を投入
      function loadImage(src) {
        return new Promise(function (resolve, reject) {
          const img = new Image()
          img.onload = function () { resolve(img) }
          img.onerror = function (e) { reject(e) }
          img.src = src
        })
      }
    </script>
  </body>
</html>

[1] FileをImageにする
FileReaderでFileの中身を取り出し、空のImageに投入します。
このあたりでPromiseを多用しているためIE用のPolyfillを入れています。

    <script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js"></script>
      // ..略
      function imageToCanvas (imageFile) {
        // [1] Fileから画像データを得る
        return new Promise(function (resolve, reject) {
          readImage(imageFile).then(function (src) {
            loadImage(src).then(function (image) {
            // ここに画像データ(image)が渡ってくるので処理
            }).catch(function (error) {
              reject(error)
            })
          }).catch(function (error) {
            reject(error)
          })
        })
      }
      // [1-A] Fileの中身を得る
      function readImage(image) {
        return new Promise(function (resolve, reject) {
          const reader = new FileReader()
          reader.onload = function () { resolve(reader.result) }
          reader.onerror = function (e) { reject(e) }
          reader.readAsDataURL(image)
        })
      }
      // [1-B] 空の画像データを作った後に中身を投入
      function loadImage(src) {
        return new Promise(function (resolve, reject) {
          const img = new Image()
          img.onload = function () { resolve(img) }
          img.onerror = function (e) { reject(e) }
          img.src = src
        })
      }

[2] HTML上のCanvasの呼び出し
キャンバスと、その描画用のコンテキストを呼び出します。

              // [2] Canvasの呼び出し
              const canvas = document.getElementById("canvas")
              const ctx = canvas.getContext('2d')

[3] 縮尺の計算
アスペクト比を維持したいので、縦横どちらか大きい方の縮小率を採用します。

              // [3] 縮尺を計算
              const scale = Math.max((canvas.height / image.height), (canvas.width / image.width))

[4] 画像を切り取り、Canvasに展開
CanvasのメソッドdrawImageを使って画像を貼り付け。
この時画像の切り取り範囲とCanvasへの貼付け範囲を一気に指定する。混乱しがち。
本当はここで画像にしたかったが、ブラウザによってこの後のデータの取り出し方が違うのでここではCanvasを返す。

              // [4] 画像を切り取り、Canvasに展開して返す
              ctx.drawImage(
                // Canvasに貼り付ける画像
                image, 
                // 画像の切り取り開始位置(左上からの横距離、縦距離)
                (image.width - (canvas.width / scale)) / 2, (image.height - (canvas.height / scale)) / 2,
                // 画像の切り取り範囲(幅、高さ)
                canvas.width / scale, canvas.height / scale,
                // キャンバスの貼り付け開始位置(左上からの横距離、縦距離)
                0, 0,
                // 画像への貼り付け範囲(幅、高さ)
                canvas.width, canvas.height
              )
              resolve(canvas)

[5] Canvasから画像を取得
Canvasにリサイズ後の画像が表示され、Canvasの各種メソッドでDataURLやBlobが取得出来る。
サンプルではブラウザからダウンロードできるようにした。IEでもいけますがpng固定になるので注意。

          // [5] キャンバスから画像を取得
          if (canvas.msToBlob) { // IEの場合
            window.navigator.msSaveBlob(canvas.msToBlob(), 'resized.png')
          } else { // その他ブラウザ
            const a = document.createElement('a')
            a.href = canvas.toDataURL(file.type)
            a.download = 'resized.' + file.type.split('/')[1]
            a.click()
          }

以上です。

0
0
1

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?