djangoで画像のドラッグアンドドロップの実装2 -Javascript(jQuery)でモーダルダイアログの作成-

djangoのプロフィール画像設定で、ドラッグアンドドロップを用いたプロフィール画像の編集ができるコードを紹介します。最終的には画像をドラッグアンドドロップしてサーバー側(djangoのviews)でsaveまでを行うつもりですが、一度に紹介するのは冗長になりますので前回続きの記事の後半部分を紹介します。

前回記事↓↓

完成イメージ

今回の記事ではまだviewsまでは到達しません。

モーダルダイアログを利用した画像の表示までを進めます。

プロフィール写真をドラッグしたら、モーダルダイアログが出現します。

過去記事参考↓↓

こんな人向け

以下の人向けに記事を書いています。

・javascript(jQuery)でドラッグアンドドロップを実装したい

・djangoでドラッグアンドドロップでサーバーに画像を保存したい

環境

環境は以下になります。ちなみにヒロヤンの場合はpipenvでの環境開発を行っています。

環境

・OS Mac

・python 3.8

・django 2.2

・jQuery 3.5.1

実際のコード

それでは早速紹介をしていきます。

HTML

前回からの追記としてModal dialogで囲まれた箇所になります。

sample.html

<body>
<h1>プロフィール画像のアップロード</h1>

<table class="edit-profile-table">
    <tr>
        <td>
            <img src="{{ MEDIA_URL }}{{ avator }}" alt="" class="avator">
        </td>
    </tr>
    <tr>
        <td>
            <div id="dragandrophandler">ここにプロフィール写真をドラッグ</div>
            <div id="preview"></div>
            <form action="" method="post" enctype="multipart/form-data"></form>
            {# csrf_token #}
            <div>{{ form.avator }}{{ form.avator.errors }}</div>
            <button type="submit" class="btn">登録</button>
            </form>
        </td>
    </tr>
</table>

<!-- Modal dialog -->
<div class="popup" id="js-popup">
    <div class="popup-inner">
        <div class="close-btn" id="js-close-btn">
            <div class="close-icon"></div>
        </div>
        <div class="container">
            <a href="#">
                <div id="popup-preview"></div>
            </a>
            <p class="item">こちらの画像を登録しますか?</p>
            <button class="item btn" id="popup-btn-y">はい</button>
            <button class="item btn" id="popup-btn-n">いいえ</button>
        </div>
    </div>
    <div class="back-background" id="js-black-bg"></div>
</div>

<!-- Modal dialog -->



{% endblock %}
{% load static %}
{% block extra_js %}
<script src="{% static 'sample/js/sample.js' %}"></script>
{% endblock %}


CSS

html同様に/* modal dialog */で囲んだところが新たに追記したコードになります。

sample.css

/* profile */

.edit-profile-table{
    box-sizing: border-box;
    border: 1px solid rgba(241, 70, 233, 0.377);
    border-radius: 5px;
    text-align: center;
    margin: 0 auto;
}

.edit-profile-table tr th ul{
    text-align:left;
}

.edit-profile-table tr td .avator{
    margin: 15px;
    border-radius: 50%;
    border: 3px solid #f1a2e7;
}


/* drag and drop */
#dragandrophandler{
    border: 2px dotted #0B85A1;
    height: 100px;
    color: #92AAB0;
    text-align: left;
    padding: 10px;
    margin-bottom: 10px;
    font-size: 2em;
}

.edit-profile-table tr td div,
.edit-profile-table tr td button{
    margin: 13px;
}
/* drag and drop */

/* modal dialog */
.popup{
    position: fixed;
    box-sizing: border-box;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    z-index: 9999;
    opacity: 0;
    visibility: hidden;
    transition: .6s;
}

.popup.is-show{
    opacity: 1;
    visibility: visible;
}

.popup-inner{
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 270px;
    max-width: 600px;
    padding: 50px;
    background-color: #fff;
    z-index: 2;
}

.close-btn{
    position: absolute;
    right: 0;
    top: 0;
    width: 50px;
    height: 50px;
    line-height: 50px;
    text-align: center;
    cursor: pointer;
}

.close-btn .close-icon{
    font-size: 21px;
    position: relative;
    width: 1.4em;
    height: 1.4em;
    border: 10px solid #39a9d6;
    border-radius: 100%;
}

.close-btn .close-icon::before{
    position: absolute;
    top: 0.2em;
    left: 0.6em;
    width: 0.2em;
    height: 1em;
    content: "";
    background-color: #39a9d6;
    transform: rotate(45deg);
}

.close-btn .close-icon::after{
    position: absolute;
    top: 0.6em;
    left: 0.2em;
    width: 1em;
    height: 0.2em;
    content: "";
    background-color: #39a9d6;
    transform: rotate(225deg);
}

.back-background{
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0,.8);
    z-index: 1;
    cursor: pointer;
}

.popup .container{
    border: 1px dotted #f00;
    display: inline-block;
    text-align: center;
    padding: 20px;
}

.resize_prev_img{
    width: 180px;
    height: 180px;
    border-radius: 10px;
}

/* modal dialog */

JS

以下にJavascriptとjQueryでモーダルダイアログを出現させるコードを書きます。こちらも上記のhtml,cssと同様に// modal dialog以下で追記した内容がモーダルダイアログの挙動のコードになります。

sample.js

'use strict';


// drag and drop event
$(document).ready(function() {
    var obj = $("#dragandrophandler");
    obj.on('dragenter', function (e) {
        e.stopPropagation();
        e.preventDefault();
        $(this).css('border', '2px solid #0B85A1');
    });
    obj.on('dragover', function (e) {
        e.stopPropagation();
        e.preventDefault();
    });
    obj.on('drop', function(e) {
        $(this).css('border', '2px dotted #0B85A1');
        e.preventDefault();

        var files = e.originalEvent.dataTransfer.files;
        
        // Modal dialog
        popupImage(files, obj);
    });

    // Avoid opening in a browser if the file is dropped outside the div
    $(document).on('dragenter', function (e) {
        e.stopPropagation();
        e.preventDefault();
    });

    $(document).on('dragover', function (e) {
        e.stopPropagation();
        e.preventDefault();
        obj.css('border', '2px dotted #0B85A1');
    });


    $(document).on('drop', function (e) {
        e.stopPropagation();
        e.preventDefault();
    });
    
})

// modal dialog
function popupImage(files, obj) {
    var popup = document.getElementById('js-popup');
    if (!popup) return;

    var fd = new FormData();
    var filetype = files[0].type;

    // Validation
    // 1. File format
    if (!(filetype.match('^image\/(png|jpeg)$'))){
        $('#dragandrophandler').after('<ul class="errorlist"><li>ファイル形式が、pngもしくはjpeg以外のものは使用できません。</li></ul>');
        return false;
    }

    // 2. File size
    var sizeKB = files[0].size / 1024;
    if (parseInt(sizeKB) > 1024) {
        var sizeMB = sizeKB / 1024;
        if (sizeMB > 20) {
            $('#dragandrophandler').after('<ul class="errorlist"><li>画像サイズが大きすぎます。20MBより小さいサイズの画像をお願いします。</li></ul>');
            // alert('画像サイズが大きすぎます。20MBより小さいサイズの画像をお願いします。');
            return false;
        };
    };

    fd.append('avator', files[0])

    // preview
    var fileReader = new FileReader();
    fileReader.onloadend = function () {
        $('#popup-preview').html('<img src="' + fileReader.result + '"/>');
        // リサイズのクラス
        $('#popup-preview img').addClass('resize_prev_img', 'item');
    }
    fileReader.readAsDataURL(files[0]);

    // 閉じる
    var blackBg = document.getElementById('js-black-bg');
    var closeBtn = document.getElementById('js-close-btn');
    var showBtn = document.getElementById('js-show-popup');
    var popupBtnN = document.getElementById('popup-btn-n');

    popup.classList.add('is-show');

     // btn yesのときの処理
    $('#popup-btn-y').off('click');
    $('#popup-btn-y').on('click', function () {
         // $('ul.errorlist').remove();
         // サーバーにデータ送信
        sendFileToServer(fd, status);
        popup.classList.remove('is-show');
        $('#dragandrophandler+div.statusbar').remove();
    });

    // 関数呼び出し
    closePopUp(blackBg);
    closePopUp(closeBtn);
    closePopUp(showBtn);
    closePopUp(popupBtnN);
    // closeボタン
    function closePopUp(elem) {
        if (!elem) return;
        elem.addEventListener('click', function () {
            popup.classList.remove('is-show');
            $('#dragandrophandler+div.statusbar').remove();
        });
    };
};

補足説明を以下にします。

  1. 4-44行目までが、ドラッグアンドドロップした時のUIの移り変わりの動作になります。
  2. 54-70行目でドロップされた画像のバリデーションをかけています。
  3. 56-59行目では、正規表現でpngファイル、もしくはjpegファイル以外がドロップされた場合は、警告のerrorlistを表示し、ドロップできないように制御しています。
  4. 62-70行目ではドロップされた画像が20MB以上の場合は、警告のerrorlistを表示し、ドロップできないように制御しています。
  5. 75-87行目がプレビュー表示についてです。
  6. 84-87行目でモーダルダイアログが表示された時の閉じる一覧の変数定義をしています。
  7. 92-99行目でモーダルダイアログが表示され、93行目で「はい」でクリックされ、96行目でサーバーに画像データを関数を呼び出していますが、冗長的になるので今回はこれ以上のコードは書いておりません。次回記事で説明します。
  8. 102-113行目で閉じる関数を呼び出しています。

以上です。これで最初の完成イメージの通りにモーダルダイアログが実装されます!

最終の完成版の実装は以下のリンク先から紹介しています。

プログラミング学習を効率良く進めるには…

私ヒロヤンがプログラミングを始めた頃は以下のような感じでした。

何から手をつけていいのか、わからない。

プログラミングを始めてみたけど、エラーが解決できない。

そしてネットで調べていくうちに膨大な時間が過ぎていきました。

私ヒロヤンの実体験より、プログラミングを効率的に学ぶために大切なことは以下のことだと考えています。

1. いつまでもダラダラとやらないで、目標を決定して短期集中する

2. マンツーマンで、わからない箇所は直ぐに質問をして即レスをもらう

.proでは私ヒロヤンが学習してきたプログラミング経験0からのpython/django、その他webサイト・サービス開発のコースが用意されています。

カウンセリング自体は無料なので話を聞いてみるだけでもいかがでしょうか?

また以下のリンク先ではdjangoを教えてくれるスクールをまとめ紹介しています。

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です