2
var img1 = new Image();
var img2 = new Image();
var img3 = new Image();
var img4 = new Image();
var imageCount = 0, NUM_IMAGES=4;
var ctx;
var WIDTH;
var HEIGHT;

 //drag
var draggable = false;
var startX;
var startY;
//array of images
var images=[];
images.push({image:img1, x:0, y:20, width:550, height:466});
images.push({image:img2, x:476, y:170, width:230, height:240});
images.push({image:img3, x:500, y:20, width:635, height:313});
images.push({image:img4, x:910, y:328, width:219, height:134});


function init(){
  ctx = $('#myCanvas')[0].getContext("2d");
  WIDTH = $("#myCanvas").width();
  HEIGHT = $("#myCanvas").height();
  ctx.fillStyle = "#A0DCE5";
  ctx.fillRect(0,0,WIDTH,HEIGHT);
  loadResources();
  $('#myCanvas').mousedown(onMouseDown);
  $('#myCanvas').mouseup(onMouseUp);
  $('#myCanvas').mousemove(onMouseMove);


}

function startInteraction() {
    imageCount++;
    if (imageCount ==NUM_IMAGES)
        render();
}

function loadResources() {
    img1.onload = startInteraction;
    img1.src = "america.png";
    img2.onload = startInteraction;
    img2.src = "africa.png";
    img3.onload = startInteraction;
    img3.src = "europe and asia.png";
    img4.onload = startInteraction;
    img4.src = "australia.png";
}
function image(image,x,y,width,height) {
    ctx.drawImage(image,x,y,width,height)
}

function render() {
    for(var i=0;i<images.length;i++){
            var r=images[i];
            image(r.image,r.x,r.y,r.width,r.height);
        }

}

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 image
    d = 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 is dragging=true
            draggable=true;
        }
    }
    //save mouse position
    startX=mx;
    startY=my;
}

function onMouseUp(e){
    //tell browser we're handling this mouse event
    e.preventDefault();
    e.stopPropagation();

    draggable = false;
    for(var i=0;i<images.length;i++){
        images[i].isDragging=false;
    }
}

function onMouseMove(e){
    //if we can drag an image
    if(draggable){

        //tell brower 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;
            }
        }
        //re render the images
        render();

        //reset the mouse positions for next mouse move;
        startX = mx;
        startY = my;
    }
}



$(document).ready(init);

I am having real difficulty trying to get images to move and every tutorial i look at I just can seem to understand it.

Is there something blindingly obvious I'm doing wrong?

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • Here's an explanation with example code: http://stackoverflow.com/questions/24926028/drag-and-drop-multiple-objects-in-html5-canvas/24938627#24938627. The example uses rectangles but you can substitute images. – markE Dec 14 '14 at 18:02
  • @markE for the image array is this the right format?http://pastebin.com/bgHGgyz8 – John Sumner Dec 14 '14 at 18:23
  • You're close! ... You must label every element in a javascript object so your img1 would be image:img1. For example: images.push({image:img1, x:550-225, y:486-233, width:550, height:466}); and then you do drawImage instead of fillRect: ctx.drawImage(r.image,r.x,r.y); Good luck with your project! – markE Dec 14 '14 at 21:38
  • @markE so far I have this but it isn't dragging anything is there anything obvious I am doing wrong? http://pastebin.com/GjYMZqYg – John Sumner Dec 14 '14 at 21:39
  • I posted an answer that shows how to (1) preload all images, (2) create javascript objects that define your images, (3) create event handlers that use the objects to drag images on the canvas. Good luck with your project! – markE Dec 14 '14 at 23:04

1 Answers1

4

When dragging 1+ images on a canvas, the procedure is:

In mousedown:

  • get the current mouse position
  • set the isDragging flag on any image that is under the mouse
  • save the current mouse position

In mousemove:

  • get the current mouse position
  • calculate how far the mouse has moved ( distance = newMousePosition-oldMousePosition )
  • add the distance to the position of any image that isDragging
  • save the current mouse position
  • redraw the scene with images in their new positions

In mouseup and mouseout:

  • clear all isDragging flags

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 the images.</h4>
<canvas id="myCanvas" width=400 height=400></canvas>
markE
  • 102,905
  • 11
  • 164
  • 176
  • just one last question would it be possible to only move 1 image at a time even if they are overlapped? – John Sumner Dec 15 '14 at 00:41
  • 1
    Sure, for example you could stop testing after you've found the first image under the mouse. You can do that by exiting the for-loop by adding `break;` after `isDragging=true;` in mousedown. Cheers! – markE Dec 15 '14 at 00:55