【JavaScript】Cropper.js にローカルの画像ファイルを渡し、切り抜き結果をファイルとしてフォームで送信する

 Cropper.js は画像をウェブページ上で加工するための JavaScript のライブラリです。人気で古くから続くライブラリで npm のみならず CDN でも配信されています。Cropeer.js のソースコードとデモは次にあります。

fengyuanchen/cropperjs: JavaScript image cropper.
Cropper.js#デモ

 Cropper.js の使い方は様々です。使い方の一つとして画像のサイズや比率が決まっている中でユーザーにそのフォーマットを守らせつつ任意の画像をアップロードさせる、という使い方があります。これを行う時はユーザーのローカルの画像ファイルをHTML上のimg要素に展開し、そのimg要素にCropper.jsをかけ、Cropper.jsの結果をinput要素に出力し、それを送信する、とする必要があります。これを実際に作ったデモとソースコードが次です。

ソースコード
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Cropper.js ファイルから読み込みデモ</title>
  <!-- CDNで Cropper.js を取得 -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.12/cropper.min.js" integrity="sha512-ooSWpxJsiXe6t4+PPjCgYmVfr1NS5QXJACcR/FPpsdm6kqG1FmQ2SVyg2RXeVuCRBLr0lWHnWJP6Zs1Efvxzww==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.12/cropper.css" integrity="sha512-+VDbDxc9zesADd49pfvz7CgsOl2xREI/7gnzcdyA9XjuTxLXrdpuz21VVIqc5HPfZji2CypSbxx1lgD7BgBK5g==" crossorigin="anonymous" referrerpolicy="no-referrer" />
  <script>
  document.addEventListener('DOMContentLoaded', function (){
    /** @var {Cropper|null} Cropperインスタンスを保持する関数 */
    let cropper = null
    document.querySelector('input[name="src-img"]').addEventListener('change', function(changeFileEvent){
      if(cropper){
        // 既に Cropper で何がしかの切り抜きをしている場合、それを入れ替えるために既存インスタンスを削除します
        cropper.destroy();
      }
      // 入力された画像ファイルを Cropper で扱う img 要素に渡します
      // FileReader を用いることで入力されたファイルを DataURI にすることでできます
      // @see https://developer.mozilla.org/ja/docs/Web/API/FileReader/readAsDataURL
      const fReaderForURI = new FileReader();
      fReaderForURI.readAsDataURL(changeFileEvent.target.files[0]);
      fReaderForURI.onload = () => {
        // 生成した DataURI を img 要素に渡し、それに対して Cropper を用意します
        const imgEl = document.getElementById('cropper-tgt');
        imgEl.src = String(fReaderForURI.result);
        cropper = new Cropper(imgEl, {aspectRatio: 16 / 9});
      }

      document.getElementById('btn-crop-action').addEventListener('click', function(){
        // Cropper インスタンスから現在の切り抜き範囲の画像を canvas 要素として取れます。
        /** @var {HTMLCanvasElement} croppedCanvas */
        const croppedCanvas = cropper.getCroppedCanvas();
        // canvas要素にはimg要素のsrcプロパティに渡した時に画像として表示される形式のデータを返すメソッド toDataURL があります。
        // これをimg要素に渡すことで切り抜き結果を画面に表示できます。
        // @see https://developer.mozilla.org/ja/docs/Web/API/HTMLCanvasElement/toDataURL
        document.getElementById('preview').src = croppedCanvas.toDataURL()

        // canvas 要素には描画されているデータを Blob としてを扱える様にするメソッド toBlob があります。
        // これを img 要素に渡すことで切り抜き結果を画面に表示できます。
        // @see https://developer.mozilla.org/ja/docs/Web/API/HTMLCanvasElement/toDataURL
        croppedCanvas.toBlob(function(imgBlob){
          // Blob を元に File 化します。
          const croppedImgFile = new File([imgBlob], '切り抜き画像.png' , {type: "image/png"});
          // DataTransfer インスタンスを介することで input 要素の files に
          // JavaScript 内で作った File を渡せます。
          // 直に new FileList から作って渡そうとすると失敗します。
          const dt = new DataTransfer();
          dt.items.add(croppedImgFile);
          document.querySelector('input[name="cropped-img"]').files = dt.files;
        });
      })
    })
  })
  </script>
  <style>
      .control {
          display: flex;
          flex-direction: column;
          width: fit-content;
          gap: 8px;
      }
      #cropper-tgt {
          max-width: 100%;
      }
      .cropper-area {
          max-width: 60vw;
          display: flex;
          flex-direction: column;
          gap: 8px;
      }
      .cropped-form{
          border-top: dashed 2px black;
          margin-top: 1em;
          display: flex;
          justify-content: center;
          flex-direction: column;
          gap: 8px;
      }
      .caption {
          font-weight: bold;
          font-size: 1.2em;
      }
  </style>
</head>
<body>
<div class="cropper-area">
  <span class="caption">Cropper.jsによる切り抜き</span>
  <img id="cropper-tgt">
  <div class="control">
    <label>画像ファイル<input type="file" name="src-img" accept="image/*"></label>
    <button type="button" id="btn-crop-action">切り取り</button>
  </div>
</div>

<form class="cropped-form" action="" enctype="multipart/form-data" method="post">
  <span class="caption">送信フォーム</span>
  <img id="preview">
  <label>切り抜き後画像ファイル<input type="file" name="cropped-img" accept="image/*"></label>
  <button type="submit">画像を送信</button>
</form>
</body>
</html>

 入力されたファイルを元に Cropper を動かす部分を抜き出すと次です。

    document.querySelector('input[name="src-img"]').addEventListener('change', function(changeFileEvent){
      // 入力された画像ファイルを Cropper で扱う img 要素に渡します
      // FileReader を用いることで入力されたファイルを DataURI にすることでできます
      // @see https://developer.mozilla.org/ja/docs/Web/API/FileReader/readAsDataURL
      const fReaderForURI = new FileReader();
      fReaderForURI.readAsDataURL(changeFileEvent.target.files[0]);
      fReaderForURI.onload = () => {
        // 生成した DataURI を img 要素に渡し、それに対して Cropper を用意します
        const imgEl = document.getElementById('cropper-tgt');
        imgEl.src = String(fReaderForURI.result);
        cropper = new Cropper(imgEl, {aspectRatio: 16 / 9});
      }
    }
  <img id="cropper-tgt">
  <div class="control">
    <label>画像ファイル<input type="file" name="src-img" accept="image/*"></label>
    <button type="button" id="btn-crop-action">切り取り</button>
  </div>

 JavaScript には FileReader というユーザーのローカルにあるファイルを読み取る機能があります。これを用いてファイルの中身を DataURI 化して img 要素の src プロパティに渡すことでローカルファイルを画像として表示できます。ここまでこれば普段の Cropper,js 同様です。Cropper.js の切り抜き対象にローカルファイルを展開した img 要素を指定して切り抜きを開始できます。

 出力については以前の記事が詳しいです。Cropper.js の機能で切り抜いた部分を canvas にして、それを img 要素、input 要素に渡しているだけです。渡した時点でプレビュー画像が見えるファイルセット済みフォームができあがっており、後は送信するだけです。

【JavaScript】画像切り抜き用ライブラリ Cropper.js の紹介と Cropper.js の結果をimg要素、input[type=”file”]要素に出力する方法 – 株式会社シーポイントラボ | 浜松のシステム・RTK-GNSS開発

>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG