0

How do I drag and drop multiple images from one canvas to another? My manager has given me this task, it's been 3 days and I am not able to do this as I'm new to HTML5. I've searched Google but only got it working for one image. Please help me in this matter.

This is what I got for one image:

<pre>
<script>
    window.onload = function ()
    {
        var canvas1 = document.getElementById("cvs1");
        var canvas2 = document.getElementById("cvs2");
        var context1 = canvas1.getContext('2d');
        var context2 = canvas2.getContext('2d');
        var imageXY  = {x: 5, y: 5};

        /**
        * This draws the image to the canvas
        */
        function Draw ()
        {
            //Clear both canvas first
            context1.clearRect(0,0,canvas1.width,canvas1.height);
            context2.clearRect(0,0,canvas2.width,canvas2.height);

            //Draw a red rectangle around the image
            if (state && state.dragging) {
                state.canvas.getContext('2d').strokeStyle = 'red';
                state.canvas.getContext('2d').strokeRect(imageXY.x - 2.5,
                                                         imageXY.y - 2.5,
                                                         state.image.width + 5,
                                                         state.image.height + 5);
            }

            // Now draw the image
            state.canvas.getContext('2d').drawImage(state.image, imageXY.x, imageXY.y);
        }

        canvas2.onclick =
        canvas1.onclick = function (e)
        {

            if (state && state.dragging) {
                state.dragging = false;
                Draw();
                return;
            }

            var mouseXY = RGraph.getMouseXY(e);

            state.canvas    = e.target;

            if (   mouseXY[0] > imageXY.x
                && mouseXY[0] < (imageXY.x + state.image.width)
                && mouseXY[1] > imageXY.y
                && mouseXY[1] < (imageXY.y + state.image.height)) {

                state.dragging       = true;
                state.originalMouseX = mouseXY[0];
                state.originalMouseY = mouseXY[1];
                state.offsetX         = mouseXY[0] - imageXY.x;
                state.offsetY         = mouseXY[1] - imageXY.y;

            }
        }

        canvas1.onmousemove =
        canvas2.onmousemove = function (e)
        {

            if (state.dragging) {

                state.canvas = e.target;

                var mouseXY = RGraph.getMouseXY(e);

                // Work how far the mouse has moved since the mousedon event was triggered
                var diffX = mouseXY[0] - state.originalMouseX;
                var diffY = mouseXY[1] - state.originalMouseY;

                imageXY.x = state.originalMouseX + diffX - state.offsetX;
                imageXY.y = state.originalMouseY + diffY - state.offsetY;

                Draw();

                e.stopPropagation();
            }
        }

        /**
        * Load the image on canvas1 initially and set the state up with some defaults
        */
        state = {}
        state.dragging     = false;
        state.canvas       = document.getElementById("cvs1");
        state.image        =  new Image();
        state.image.src    = 'images/logo.png';
        state.offsetX      = 0;
        state.offsetY      = 0;


        state.image.onload = function ()
        {
            Draw();
        }
    }
</script>

<canvas id="cvs1" width="400" height="125" style="float: left">[No canvas support]</canvas>
<canvas id="cvs2" width="400" height="125" style="float: left; margin-left: 100px">[No canvas support]</canvas>
</pre>
Esoteric Screen Name
  • 6,082
  • 4
  • 29
  • 38
Vishrant
  • 15,456
  • 11
  • 71
  • 120

1 Answers1

2

Drag/Drop multiple items between 2 canvases

Here’s what the code does:

  • Click to select one or more images from the top source canvas
  • Click an image again to toggle its selection on/off
  • Once you have made all your selections, drag from the top canvas to the bottom canvas
  • Your selections will be moved to the bottom canvas

enter image description here

Some explanation about the code:

  • Each image is stored in an array called Images
  • An item-object for each image is stored in an array called items
  • The item-object contains an item’s description, image-url, an isSelected flag and an isDropped flag.
  • The mouseup event handler of the top source canvas checks for hits on images and toggles their isSelected flags.
  • The mouseup event handler responds to drops onto the bottom drop canvas. It checks for selected items and records them as dropped by setting their isDropped flags.
  • The drawContainer function distributes items between the source and drop canvas based on their isDropped flags (isDropped==false are drawn in the top source canvas – isDropped==true are drawn in the bottom drop canvas)

Here is code and a Fiddle: http://jsfiddle.net/m1erickson/3KqgX/

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; padding:10px; }
    canvas{border:1px solid red;}
    #canvas { 
    }
    #canvas:active { 
        cursor: move;
    }
</style>

<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");
    var drop=document.getElementById("dropzone");
    var dropCtx=drop.getContext("2d");

    var canvasOffset=$("#canvas").offset();
    var offsetX=canvasOffset.left;
    var offsetY=canvasOffset.top;

    var mouseIsDown=false;
    var frameWidth=128;
    var frameHeight=128;
    // checkmark for selected
    var checkmark=document.createElement("img");
    checkmark.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/smallCheckmark.png";

    var images=[];
    var items=[];
    items.push({description:"House#1",url:"https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house1.jpg",isSelected:false,isDropped:false,x:0,y:0});
    items.push({description:"House#2",url:"https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house2.jpg",isSelected:false,isDropped:false,x:0,y:0});
    items.push({description:"House#3",url:"https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house3.jpg",isSelected:false,isDropped:false,x:0,y:0});


    var imgLoadCount=0;
    for(var i=0;i<items.length;i++){
        images[i]=document.createElement("img");
        images[i].onload=function(){
            if(++imgLoadCount>=items.length){ draw(); }
        }
        images[i].src=items[i].url;
    }

    function draw(){

        ctx.clearRect(0,0,canvas.width,canvas.height);
        dropCtx.clearRect(0,0,drop.width,drop.height);

        var canvasX=0;
        var dropX=0;
        // 
        for(var i=0;i<items.length;i++){
            if(items[i].isDropped){
                x=dropX*160+10;
                drawContainer(dropCtx,i,x,20);
                dropX++;
                items[i].x=x;
            }else{
                x=canvasX*160+10;
                drawContainer(ctx,i,x,20);
                canvasX++;
                items[i].x=x;
            }
        }
    }


    // draw image container
    function drawContainer(context,index,x,y){

        context.beginPath();
        context.rect(x,y+frameHeight,frameWidth,30);
        context.fillStyle="black";
        context.fill();
        context.beginPath();
        context.fillStyle="white";
        context.font="10pt Verdana";
        context.fillText(items[index].description,x+10,y+frameHeight+18);

        // draw a thumbnail of the image
        var img=images[index];
        if(img.width>=img.height){
            context.drawImage(img,0,0,img.width,img.height,
                x,y,128,128*img.height/img.width);
        }else{
            context.drawImage(img,0,0,img.width,img.height,
                x,y,128*img.width/img.height,128);  // edited s/b [,128], not [/128]
        }

        // outer frame (green if selected)
        context.beginPath();
        context.rect(x-2,y-2,frameWidth+4,frameHeight+30+4);
        context.lineWidth=3;
        context.strokeStyle="lightgray";
        if(items[index].isSelected){
            context.strokeStyle="green";
            context.drawImage(checkmark,x+frameWidth-30,y+frameHeight+3);
        }
        context.stroke();

    }

    function handleMouseDown(e){
        mouseX=parseInt(e.clientX-offsetX);
        mouseY=parseInt(e.clientY-offsetY);

        // Put your mousedown stuff here
        mouseIsDown=true;
    }

    function handleMouseUp(e){
      mouseIsDown=false;
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);

      // Put your mouseup stuff here
      for(var i=0;i<items.length;i++){
          var item=items[i];
          // have we clicked on something?
          if(!item.isDropped && mouseX>=item.x && mouseX<=item.x+frameWidth){
              // if so, toggle its selection
              items[i].isSelected=!(items[i].isSelected);
              draw();
          }
      }
    }

    function handleMouseOut(e){
      if(!mouseIsDown){return;}
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);

      // Put your mouseOut stuff here
    }

    function handleMouseMove(e){
      mouseX=parseInt(e.clientX-offsetX);
      mouseY=parseInt(e.clientY-offsetY);

      // Put your mousemove stuff here

    }

    function handleDrop(e){
        for(var i=0;i<items.length;i++){
            if(items[i].isSelected){
                items[i].isDropped=true;
                items[i].isSelected=false;
                console.log(i);
            }
        }
        draw();
    }

    $("#canvas").mousedown(function(e){handleMouseDown(e);});
    $("#canvas").mousemove(function(e){handleMouseMove(e);});
    $("#canvas").mouseup(function(e){handleMouseUp(e);});
    $("#canvas").mouseout(function(e){handleMouseOut(e);});
    $("#dropzone").mouseup(function(e){handleDrop(e);});

}); // end $(function(){});
</script>

</head>

<body>
    <p>Click an item to toggle it's selection</p>
    <p>Drag from top to bottom canvas to drop selected items</p>
    <canvas id="canvas" width=500 height=200></canvas><br>
    <canvas id="dropzone" width=500 height=200></canvas>
</body>
</html>

[Addition: Alternate code to sort bottom canvas by order dropped]

    function handleDrop(e){
        for(var i=items.length-1;i>=0;i--){
            if(items[i].isSelected){
                items[i].isDropped=true;
                items[i].isSelected=false;
                // sort the bottom canvas by order dropped
                var move=items[i];
                items.splice(i,1);
                items.push(move);
            }
        }
        draw();
    }

[ Edited to present a solution in KineticJS ]

Here is code and a Fiddle: http://jsfiddle.net/m1erickson/bSpBF/

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
    <script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.5.1.min.js"></script>
<style>
    body{ background-color: ivory; padding:10px;}
    #container1,#container2{
      border:solid 1px #ccc;
      margin-top: 10px;
      width:300px;
      height:100px;
    }
    #container2{
      height:300px;
    }
</style>        
<script>
$(function(){

    var highlightWidth=8;

    var stage = new Kinetic.Stage({
        container: 'container1',
        width: 300,
        height: 100
    });
    var layer = new Kinetic.Layer();
    stage.add(layer);


    var dropzone = new Kinetic.Stage({
        container: 'container2',
        width: 300,
        height: 300
    });
    var dropLayer = new Kinetic.Layer();
    dropzone.add(dropLayer);


    // these must go after the creation of stages & layers
    addBackground(stage,layer,dropLayer);
    layer.draw();
    addBackground(dropzone,dropLayer,layer);
    dropLayer.draw();


    // get images & then trigger start()
    var images={};
    var URLs = {
      house1: 'https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house204-3.jpg',
      house2: 'https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house204-4.jpg',
      house3: 'https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house204-1.jpg'
    };
    loadImages(URLs,start);


    function start(){
        var house1=kImage(images.house1,10,10,50,50,layer);
        var house2=kImage(images.house2,75,10,50,50,layer);
        var house3=kImage(images.house3,140,10,50,50,layer);
        layer.draw();
    }


    function swapStagesIfSelected(sourceLayer,destinationLayer,startX,startY){

        // get all elements on the source layer
        var elements=sourceLayer.get("Image");

        // don't let dropped elements fall off the stage
        var totalWidth=0;
        var maxHeight=-999;
        var layerWidth=destinationLayer.getStage().getWidth();
        var layerHeight=destinationLayer.getStage().getHeight();
        for(var i=0;i<elements.length;i++){
            if(elements[i].isSelected){
                totalWidth+=elements[i].getWidth();
                maxHeight=Math.max(elements[i].getHeight(),maxHeight);
            }
        }
        if(startX+totalWidth>layerWidth){
            startX=layerWidth-totalWidth-15; 
        }
        if(startY+maxHeight>layerHeight){
            startY=layerHeight-maxHeight-15; 
        }

        // move all selected images 
        // to the clicked x/y of the destination layer
        for(var i=0;i<elements.length;i++){
            var element=elements[i];
            if(element.isSelected){
                var img=element.getImage();
                kImage(img,startX,startY,element.getWidth(),element.getHeight(),destinationLayer);
                startX+=element.getWidth()+10;
                element.remove();
            }
        }
        sourceLayer.draw();
        destinationLayer.draw();
    }


    // build the specified KineticJS Image and add it to the specified layer
    function kImage(image,x,y,width,height,theLayer){
        var image=new Kinetic.Image({
            image:image,
            x:x,
            y:y,
            width:width,
            height:height,
            strokeWidth:0.1,
            stroke:"green",
            draggable:true
        });
        image.myLayer=theLayer;
        image.isSelected=false;
        image.on("click",function(){
            highlight(this);
            this.myLayer.draw();
        });
        image.myLayer.add(image);
        return(image);
    }


    // build a background image and add it to the specified stage
    function addBackground(theStage,theLayer,otherLayer){

        var background = new Kinetic.Rect({
          x: 0,
          y: 0,
          width: theStage.getWidth(),
          height: theStage.getHeight(),
          fill: "white",
          stroke: "green",
          strokeWidth: 1
        });
        background.on("click",function(){
            var pos=theStage.getMousePosition();
            var mouseX=parseInt(pos.x);
            var mouseY=parseInt(pos.y);
            swapStagesIfSelected(otherLayer,theLayer,mouseX,mouseY);
        });
        theLayer.add(background);
    }


    /////////////  Image loader

          function loadImages(URLs, callback) {
            var loaded = 0;
            var needed = 0;
            for(var url in URLs) { needed++; console.log(url); }
            for(var url in URLs) {
              images[url] = new Image();
              images[url].onload = function() {
                if(++loaded >= needed) {
                  callback(images);
                }
              };
              images[url].src = URLs[url];
            }
          }

    /////////////  Toggle Highlighting

    function highlight(element,setStrokeWidth){
        if(setStrokeWidth){
                element.setStrokeWidth(setStrokeWidth);
        }else{
            if(element.getStrokeWidth()>5){
                element.setStrokeWidth(0.1);
                element.isSelected=false;
            }else{
                element.setStrokeWidth(highlightWidth);
                element.isSelected=true;
            }
        }
    }


}); // end $(function(){});

</script>       
</head>

<body>
    <p>Click on image(s) to toggle selection</p>
    <p>Then click in the other canvas to drop</p>
    <div id="container1"></div>
    <div id="container2"></div>
    <button id="clear">Clear Hightlights</button>
    <button id="swap">Swap Selected</button>
</body>
</html>
markE
  • 102,905
  • 11
  • 164
  • 176
  • Thanks markE. In the above code when I am arranging the images of first canvas in other canvas, they are getting arranged in the same order as they are in canvas 1, what if i want to arrange then randomly for ex. if i drag 2nd image first it will be arranged 1st than 3rd image than 1st image. – Vishrant Jun 22 '13 at 11:46
  • You're welcome! To re-arrange the order of appearance in the dropzone, just rearrange the order of the elements in the items[] array to your desired order. – markE Jun 22 '13 at 12:48
  • but mike that will be hard coded, what if i want all this to be done dynamically. Thanks. – Vishrant Jun 24 '13 at 04:47
  • Nothing hard-coded here. ;) You can change the order of the elements in the array to whatever you desire by using array.slice. For example, I have added alternate code to my answer that will sort the images in the bottom canvas by the order they were dropped. Cheers! – markE Jun 24 '13 at 06:17
  • mark what if i want to add dragging property(i should be able to move images any where within canvas) to images that we are adding into canvas with id "dropzone" – Vishrant Jun 24 '13 at 11:26
  • as it is given at this tutorial http://www.html5canvastutorials.com/kineticjs/html5-canvas-drag-and-drop-an-image-tutorial/ Thanks. :) – Vishrant Jun 24 '13 at 11:29
  • Sure. I can recommend KineticJS as a good library for html canvas. Unlike pure canvas, KineticJS "remembers" where all your images are on the canvas so you can easily drag them to anywhere on the "stage". Since the images are remembered objects, you even get events when they are moved so you can respond to where they are on the stage. Good tutorials also. KineticJS would be a good choice. – markE Jun 24 '13 at 12:31
  • Hi mark. There is one example at http://www.rgraph.net/blog/2013/january/an-example-of-html5-canvas-drag-n-drop.html but in this example i can only drag and drop SINGLE image, actually i want to add multiple images and implement the same as given in the example at that link. how to do the same. Thanks. – Vishrant Jun 27 '13 at 05:40
  • I have added a KineticJS solution to my answer. It shows how to select multiple images from one canvas and then click-drop them onto the other canvas as in your rgraph example. You can rearrange images by dragging within a canvas. A fiddle: http://jsfiddle.net/m1erickson/bSpBF/ – markE Jun 27 '13 at 23:47
  • @markE - Hi Mark. Your latest JSFiddle has helped me greatly - however one thing I am now stuck on is adding a button that will allow me to spawn/add new images to the canvas when the user clicks some form of button. Would you be able to give me some advice regarding this please as I am not sure how to go about this? Your help is much appreciated, thanks – yonetpkbji Nov 21 '13 at 14:18
  • This code not working, I tried to copy jsfiddle code to my local and added new images and jQuery script also but not able to drag image one canvas to other canvas – Arpit Jul 10 '23 at 09:50