📝

DjangoアプリケーションにCroppieを導入する(part1)

2021/01/11に公開

開発しているWebアプリケーションに
画像のクロッピング&アップロード機能を実装することになりました。

GitHubとかTwitterのプロフィール画像で使うやつです。
クロッピング機能のサンプル
こんなやつ

※画像提供 Uschi DugulinによるPixabayからの画像

色々調べたんですが、Croppieというライブラリを導入するのが一番イメージに合いそう・・・
と、いうことで、色々と試行錯誤しなら導入しましたというお話です。
今回から、何回かに分けて書いていきたいと思います。

実装結果のサンプル画像
Bootstrap4 を利用しているので、モーダルからクロッピング&登録が出来るようにしました。
登録ボタンを押すと、クロッピングされた画像がPostされてDjango側のモデル(django.db.models.ImageField)に格納されます。

環境

ソフトウェア バージョン
Python 3.7.7
Django 3.0.6
Bootstrap 4.5.0

Croppieについて

Croppieはその名の通り、画像のクロッピングをするためのJavascriptライブラリです。
https://foliotek.github.io/Croppie/
公式にある通り、npmもしくはGithubからダウンロードして利用します。

Croppieのインストール

とりあえず、ダウンロードしたCroppieのjsとcssファイルをDjangoプロジェクトに格納します。
どちらも静的ファイルなので、staticフォルダ内に格納することになります。
例えば、myproject/myapp/static/myapp/croppie.jsみたいな感じです。

static配下に敢えてアプリケーション名のついたフォルダを挟むのは、いわばDjangoのお作法的な意味合いです。本番環境にデプロイする際、collectstaticを実行すると全ての静的ファイルがSTATIC_ROOT配下に集められます。そのため、別々のアプリケーション配下に同名ファイルがあっても衝突しないようにするための対策です。
参考:https://docs.djangoproject.com/ja/3.1/howto/static-files/deployment/

Croppieで利用したい画面上のテンプレートで、croppie.cssとcroppie.jsを読み込みます。

myapp_template.html
{% load static %}
<link rel="stylesheet" href="{% static 'myapp/croppie.css' %}" />
 途中省略
<script src="{% static 'myapp/croppie.js' %}"></script>

Djangoテンプレートの編集

Croppie自体を動作させるには、クロッピングを表示させたい領域にidなりnameなりを埋め込み、JS側で$('.my-image').croppie();みたいにしてやるだけです。
ただ、今回はBootstrapのモーダル画面で動作させるので、その実装も必要です。

myapp_template.html
<a class="btn btn-success btn-sm mb-2" href="#" data-toggle="modal" data-target="#uploadimageModal" role="button">プロフィール画像アップロード</a>
<div class="modal fade" id="uploadimageModal" tabindex="-1" role="dialog" aria-labelledby="uploadimageModalTitle" aria-hidden="true">
    {% include "modal/profile_image.html" %}
</div>
profile_image.html
<div class="modal-dialog modal-lg modal-dialog-centered" role="document">
    <div class="modal-content">
        <div class="modal-header">
            <h5 class="modal-title">プロフィール画像をアップロード</h5>
        </div>
        <div class="modal-body">
            <div class="input-group mb-3">
                <p>プロフィールに使用する画像をアップロードし、トリミングを行った上で登録してください。</p>
                <div class="input-group-prepend">
                    <span class="input-group-text" id="inputGroupFileAddon">アップロード</span>
                </div>
                <div class="custom-file">
                    <input type="file" name="upload_image" id="upload_image" class="custom-file-input" aria-describedby="inputGroupFileAddon" accept="image/*" />
                    <label class="custom-file-label" for="inputGroupFile" data-browse="参照">ファイル選択...</label>
                </div>
            </div>
            <div id="profileImage_croppie"></div>
        </div>
        <div class="modal-footer">
            <button class="btn btn-primary btn-sm" id="post_image">画像を登録</button>
            <button type="button" class="btn btn-secondary btn-sm ml-2" id="cancel" data-dismiss="modal">キャンセル</button>
        </div>
    </div>
</div>

色々書いてますが、Bootstrapの公式から提供されているモーダルのテンプレートを少しいじっただけです。重要なのは<div id="profileImage_croppie"></div>の所で、ここにクロッピングフォームを表示させます。

Croppieの起動イベント

あとは、JSにCroppieの起動イベントを書いてやります。
一旦、起動イベントだけです。Postした際のコードは次回にご紹介します。

profileImage.js
$(document).ready(function(){
    viewportWidth = 420 //クロッピングするサイズ(横幅 ピクセル表記)
    viewportHeight = 560 //クロッピングするサイズ(縦幅 ピクセル表記)
    boundaryWidth = 700 //クロッピング元画像のサイズ(横幅 ピクセル表記)
    boundaryHeight = 700 //クロッピング元画像のサイズ(縦幅 ピクセル表記)

    // croppieの初期設定
    $image_crop = $('#profileImage_croppie').croppie({
        enableExif: true,
        viewport: {
            width: viewportWidth,
            height: viewportHeight,
            type:'square' //円形にクロッピングしたい際はここをcircleとする
        },
        boundary: {
            width: boundaryWidth,
            height: boundaryHeight
        }
    })
});

これだけです。
簡単!・・・ここまではね(汗

初期表示にフォームを非表示にする

このままだと、元画像をアップロードする前からフォームが表示されてしまってカッコ悪いです。
画像がインプットされたタイミングにあわせてフォームも表示させてみます。

まず、初期表示の際はフォームを非表示にします。これは単にCSSで隠すだけです。

profileImage.js
// 初期表示時はCroppieを非表示
$('#profileImage_croppie').ready(function(event){
    $('#profileImage_croppie').css('display','none');
});

で、元画像のアップロードにあわせて再表示させます。
これはinputタグに対するchangeイベントでできます。

profileImage.js
$(document).ready(function(){
  ↓ 追記 ↓
    $('#upload_image').on('change', function(){
        var reader = new FileReader();
        reader.onload = function (event) {
            $image_crop.croppie('bind', {
                url: event.target.result
            }).then(function(){
                // console.log('Bind complete');
            });
        }
        $('#profileImage_croppie').css('display','block');
        reader.readAsDataURL(this.files[0]);
    });
});

ここまでのおさらい

これで、下記のことはできました。

  1. HTML画面表示(モーダルは非表示状態)
  2. 画像アップロードボタン押下でモーダル表示
  3. 元画像のアップロード
  4. 元画像を対象にCroppieが起動

次回

次回はクロッピングされた画像をサーバサイドへPostする所を説明します。

参考

下記のサイトを参考にしました。
https://qiita.com/1060ki/items/1692f0e275634245c48d
https://www.for-engineer.life/entry/python-croppie/

Discussion