LoginSignup
62
56

More than 5 years have passed since last update.

HTML5のcanvas内の複数の画像をドラッグ&ドロップさせてみる

Last updated at Posted at 2014-07-17

概説

HTML5のcanvas内に複数の画像を任意の順序で表示する」で描画したcanvas内の画像をドラッグ&ドロップしたい欲が湧いたので、コードを書いてみました。

ドラッグ&ドロップは通常のDOMであれば、HTML5のDrag & Drop APIを利用することで簡単に対応可能です。
しかし、canvasに一度描いてしまったオブジェクトは、DOMとして操作することができないため、座標を考慮した地味な実装が必要になります。

以下のようなサイトを参考にしながら、画像の重なり順と当たり判定を考えてやってみたところ、自分が「いかに座標に弱いか」ということがよく分かりました。。。

KineticJS」などのライブラリを使うことで、もっと簡単に実現できるようですが、今回は素のJavaScriptだけで実装することを心がけてみてます。

JavaScript
<body>
<canvas id="canvas" width="600" height="400"></canvas>
<script>
(function() {
    var canvas  = document.getElementById('canvas');
    var context = canvas.getContext('2d');
    var isDragging = false;
    var dragTarget = null; // ドラッグ対象の画像の添え字

    var srcs = [
        '1.png',
        '2.png',
        '3.png',
        '4.png',
    ];
    var images = [];
    for (var i in srcs) {
        images[i] = new Image();
        images[i].src = srcs[i];
    }

    var loadedCount = 0;
    for (var i in images) {
        images[i].addEventListener('load', function() {
            if (++loadedCount == images.length) {
                var x = 0;
                var y = 0;
                var w = 150;
                var h = 100;
                for (var j in images) {
                    // 画像を描画した時の情報を記憶(Imageのプロパティに突っ込むのはちょっと反則かもだけど)
                    images[j].drawOffsetX = x;
                    images[j].drawOffsetY = y;
                    images[j].drawWidth   = w;
                    images[j].drawHeight  = h;

                    // 画像を描画
                    context.drawImage(images[j], x, y, w, h);
                    x += 50;
                    y += 70;
                }
            }
        }, false);
    }

    // ドラッグ開始
    var mouseDown = function(e) {
        // ドラッグ開始位置
        var posX = parseInt(e.clientX - canvas.offsetLeft);
        var posY = parseInt(e.clientY - canvas.offsetTop);

        for (var i = images.length - 1; i >= 0; i--) {
            // 当たり判定(ドラッグした位置が画像の範囲内に収まっているか)
            if (posX >= images[i].drawOffsetX &&
                posX <= (images[i].drawOffsetX + images[i].drawWidth) &&
                posY >= images[i].drawOffsetY &&
                posY <= (images[i].drawOffsetY + images[i].drawHeight)
            ) {
              dragTarget = i;
              isDragging = true;
              break;
            }
        }
    }

    // ドラッグ終了
    var mouseUp = function(e) {
        isDragging = false;
    };

    // canvasの枠から外れた
    var mouseOut = function(e) {
        // canvas外にマウスカーソルが移動した場合に、ドラッグ終了としたい場合はコメントインする
        // mouseUp(e);
    }

    // ドラッグ中
    var mouseMove = function(e) {
        // ドラッグ終了位置
        var posX = parseInt(e.clientX - canvas.offsetLeft);
        var posY = parseInt(e.clientY - canvas.offsetTop);

        if (isDragging) {
            // canvas内を一旦クリア
            context.clearRect(0, 0, canvas.width, canvas.height);

            var x = 0;
            var y = 0;
            var w = 150;
            var h = 100;
            for (var i in images) {
                if (i == dragTarget) {
                    x = posX - images[i].drawWidth / 2;
                    y = posY - images[i].drawHeight / 2;

                    // ドラッグが終了した時の情報を記憶
                    images[i].drawOffsetX = x;
                    images[i].drawOffsetY = y;
                } else {
                    x = images[i].drawOffsetX;
                    y = images[i].drawOffsetY;
                }
                w = images[i].drawWidth;
                h = images[i].drawHeight;

                // 画像を描画
                context.drawImage(images[i], x, y, w, h);
            }
        }
    };

    // canvasにイベント登録
    canvas.addEventListener('mousedown', function(e){mouseDown(e);}, false);
    canvas.addEventListener('mousemove', function(e){mouseMove(e);}, false);
    canvas.addEventListener('mouseup',   function(e){mouseUp(e);},   false);
    canvas.addEventListener('mouseout',  function(e){mouseOut(e);},  false);
})();
</script>
</body>

このようにcanvas内に並んでいる画像が。。。。

ドラッグ&ドロップ前


重なり順を維持した状態でドラッグ&ドロップができるようになりました。

ドラッグ&ドロップ前

62
56
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
62
56