jqueryhtmljquery-uicanvasjcanvas

How can make a dropped image draggable within the canvas ?


http://jsfiddle.net/m1erickson/2Us2S/

**I am using this code , how can I make dropped images as draggable . I have tried with jquery but I need to drag the same within the canvas . Enlighten me . **

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;

var image1 = new Image();
image1.src = "https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house32x32transparent.png";

var $house = $("#house");
var $canvas = $("#canvas");

$house.draggable({
    helper: 'clone',
});
// set the data payload
$house.data("image", image1); // key-value pair

$canvas.droppable({
    drop: dragDrop,
});


function dragDrop(e, ui) {
    var element = ui.draggable;
    var data = element.data("url");
    var x = parseInt(ui.offset.left - offsetX);
    var y = parseInt(ui.offset.top - offsetY);
    ctx.drawImage(element.data("image"), x - 1, y);
}

Solution

  • Once you drawImage onto the canvas, the house becomes unremembered, unmovable pixels on the canvas. You can't use jQuery to drag the house any more.

    Canvas "moves" things by erasing the canvas and redrawing the contents in their changed positions.

    Canvas "drags" by listening for mouse events:

    To create a bounding box to hit test against, you need to keep track of at least the width & height of the house image and the x,y of where it was dropped on the canvas. You typically save this information in a javascript object.

    Here's annotated code and a Demo:

    // canvas related stuff
    var canvas=document.getElementById("myCanvas");
    var ctx=canvas.getContext("2d");
    var WIDTH = canvas.width;
    var HEIGHT = canvas.height;
    ctx.fillStyle = "#A0DCE5";
    $myCanvas=$('#myCanvas');
    
    //drag
    var isDragging = false;
    var startX;
    var startY;
    
    //array of image objects
    var images=[];
    var NUM_IMAGES=0;
    
    // queue up 4 test images
    addImage(20,20,0.50,'https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house204-1.jpg');
    addImage(240,20,0.50,'https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house204-2.jpg');
    addImage(20,220,0.50,'https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house204-3.jpg');
    addImage(240,220,0.50,'https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house204-4.jpg');
    
    // trigger all images to load
    for(var i=0;i<images.length;i++){
      images[i].image.src=images[i].url;
    }
    
    
    //////////////////////////////
    // functions
    //////////////////////////////
    
    // queue up another image
    function addImage(x,y,scaleFactor,imgURL){
      var img=new Image();
      img.crossOrigin='anonymous';
      img.onload=startInteraction;
      images.push({image:img,x:x,y:y,scale:scaleFactor,isDragging:false,url:imgURL});
      NUM_IMAGES++;
    }
    
    // called after each image fully loads
    function startInteraction() {
    
      // return until all images are loaded
      if(--NUM_IMAGES>0){return;}
    
      // set all images width/height
      for(var i=0;i<images.length;i++){
        var img=images[i];
        img.width=img.image.width*img.scale;
        img.height=img.image.height*img.scale;
      }
    
      // render all images
      renderAll();
    
      // listen for mouse events
      $myCanvas.mousedown(onMouseDown);
      $myCanvas.mouseup(onMouseUp);
      $myCanvas.mouseout(onMouseUp);
      $myCanvas.mousemove(onMouseMove);
    
    }
    
    // flood fill canvas and 
    // redraw all images in their assigned positions
    function renderAll() {
      ctx.fillRect(0,0,WIDTH,HEIGHT);
      for(var i=0;i<images.length;i++){
        var r=images[i];
        ctx.drawImage(r.image,r.x,r.y,r.width,r.height);
      }
    }
    
    // handle mousedown events
    function onMouseDown(e){
    
      // tell browser we're handling this mouse event
      e.preventDefault();
      e.stopPropagation();
    
      //get current position
      var mx=parseInt(e.clientX-$myCanvas.offset().left);
      var my=parseInt(e.clientY-$myCanvas.offset().top);
    
      //test to see if mouse is in 1+ images
      isDragging = false;
      for(var i=0;i<images.length;i++){
        var r=images[i];
        if(mx>r.x && mx<r.x+r.width && my>r.y && my<r.y+r.height){
          //if true set r.isDragging=true
          r.isDragging=true;
          isDragging=true;
        }
      }
      //save mouse position
      startX=mx;
      startY=my;
    }
    
    // handle mouseup and mouseout events
    function onMouseUp(e){
      //tell browser we're handling this mouse event
      e.preventDefault();
      e.stopPropagation();
    
      // clear all the dragging flags
      isDragging = false;
      for(var i=0;i<images.length;i++){
        images[i].isDragging=false;
      }
    }
    
    // handle mousemove events
    function onMouseMove(e){
    
      // do nothing if we're not dragging
      if(!isDragging){return;}
    
      //tell browser we're handling this mouse event
      e.preventDefault
      e.stopPropagation
    
      //get current mouseposition
      var mx = parseInt(e.clientX-$myCanvas.offset().left);
      var my = parseInt(e.clientY-$myCanvas.offset().top);
    
      //calculate how far the mouse has moved;
      var dx = mx - startX;
      var dy = my - startY;
    
      //move each image by how far the mouse moved
      for(var i=0;i<images.length;i++){
        var r=images[i];
        if(r.isDragging){
          r.x+=dx;
          r.y+=dy;
        }
      }
    
      //reset the mouse positions for next mouse move;
      startX = mx;
      startY = my;
    
      //re render the images
      renderAll();
    
    }
    body{ background-color: ivory; }
    canvas{border:1px solid red;}
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <h4>Drag a house with the mouse.</h4>
    <canvas id="myCanvas" width=400 height=400></canvas>