cleanest Drag and Drop code in Javascript Canvas

10,293

Here's the setup I've used for dragging a single item. I can't tell if you want to drag multiple things or not, that'd be a slight modification.

In words: in mousedown search for a hit object in the reserved order you drew the objects (so the topmost item gets hit first), store this hit item, then mousedrag is simply piping the coords/delta to that item.

//start with only the mousedown event attached
canvas.addEventListener("mousedown",mousedown);

//and some vars to track the dragged item
var dragIdx = -1;
var dragOffsetX, dragOffsetY;

function mousedown(e){
    //...calc coords into mouseX, mouseY
    for(i=circArray.length; i>=0; i--){ //loop in reverse draw order
        dx = mouseX - circArray[i].x;
        dy = mouseY - circArray[i].y;
        if (Math.sqrt((dx*dx) + (dy*dy)) < circArray[i].r) {         
            //we've hit an item
            dragIdx = i; //store the item being dragged
            dragOffsetX = dx; //store offsets so item doesn't 'jump'
            dragOffsetY = dy;
            canvas.addEventListener("mousemove",mousemove); //start dragging
            canvas.addEventListener("mouseup",mouseup);
            return;
        }
    }
}

function mousemove(e) {
     //...calc coords
     circArray[dragIdx].x = mouseX + dragOffsetX; //drag your item
     circArray[dragIdx].y = mouseY + dragOffsetY;
     //...repaint();
}

function mouseup(e) {
     dragIdx = -1; //reset for next mousedown
     canvas.removeListener(.... //remove the move/up events when done
}

My js is rusty at the moment but this should give the idea. The dragOffsetX/Y is used to keep the item from jumping to the cursor when clicked. You could also just store the old mouse coordinate and add a delta to your item.

Also, instead of storing an index to your drag item you could store a reference to it, or maybe an array of references to drag multiple items. And instead of directly manipulating your items you could put a mousedown/drag/up interface on them to let them handle it. This'll make it easier to mix in other types of items.

Another thing I'm not sure about is how you're calculating your coordinates. I do something different but it's older code and I'm guessing your way measures just as well. -t

Share:
10,293
Admin
Author by

Admin

Updated on July 26, 2022

Comments

  • Admin
    Admin almost 2 years

    I'm looking for the fastest and lightest way to drag and drop shapes and sprites on a JS Canvas for game-development purposes.

    I've started by doing distance checks using the current mouse position and the origins of circles. It works, but when they overlap we have problems and I don't know how well this will work when testing for multiple sprites and other shapes yet on each 'frame'.

    Any comments or pointers to better methods are appreciated!

    I'd rather not use a library like jQuery since I'm going for pure speed and lightness and of course to learn the actual methods! Here's where I'm at:

    //add the canvas listeners and functions
    
    canvas.addEventListener("mousemove",mousemove);
    canvas.addEventListener("mousedown",mousedown);
    canvas.addEventListener("mouseup",mouseup);
    
    function mousemove(e){
        mouseX = e.layerX - canvas.offsetLeft;
        mouseY = e.layerY - canvas.offsetTop;
    
    //for each circle stored in my array of Circle objects, is my mouse within its'           
    //bounds? If so, set the circles' (X,Y) to my mouse's (X,Y)
    
        for(i=0;i<circArray.length;i++){
            dx = mouseX - circArray[i].x;
            dy = mouseY - circArray[i].y;
            dist = Math.sqrt((dx*dx) + (dy*dy));
            if(draggable && dist < circArray[i].r){         
                circArray[i].x = mouseX;
                circArray[i].y = mouseY;
            }
        }
    }
    
    function mousedown(){
            draggable = true;
    }
    
    function mouseup(){
            draggable = false;
    }
    
  • JaAnTr
    JaAnTr over 7 years
    I am pretty sure that circArray[dragIdx].x = mouseX + dragOffsetX; should actually be 'circArray[dragIdx].x = mouseX - dragOffsetX;'. So that the dragOffset is being subtracted from the mouse location.