Dragging/Moving shapes smoothly using Canvas 2d APIs


Did you ever write code to drag or move shapes using HTML5 Canvas 2d APIs? If you did, then didn't you ever face a situation (an unexpected behaviour) when you drag or move a shape (e.g. circle, rectangle, image, Bezier cubic/quadratic curve, etc.)?

Fore a live example, please test following demo. Drag any shape (from its any corner):


Didn't you face a sudden jerk on the shape?

Test bottom-part of the demo too. It is working smoothly. There is no sudden jerk!

Let us dig into both examples

circleX = e.pageX - canvas.offsetLeft;
circleY = e.pageY - canvas.offsetTop;

context.arc(circleX, circleY, 50, 0, Math.PI * 2, false);

Each workaround regarding the shape's width/height or radius is inefficient or non-smooth:
  • Extraction of the radius (one or two times) to accurately position the circle
  • Subtraction of half width/height of the shape (rectangle/image) to position it accordingly
Second example/demo is the solution!

But for second example, I've not used pageX/pageY directly; instead I'm doing following steps:

  • Tracking first/previous mouse-down or touch position (x-y coordinates)
  • Extracting the dragged distance from those coordinates
  • Adding or subtracting the distance from shape's coordinates according to the dragging direction (positive or negative x-y axis)


/* Global Variable */
var previousPoints = [0, 0];

/* Mouse Down event! */
{
    var x = ( e.pageX - canvas.offsetLeft ), y = ( e.pageY - canvas.offsetTop );
    
    /* Saving last x-y coordinates! - on mousedown */
    previousPoints = [x, y];
}

/* Mouse Move event! */
{    
    var x = e.pageX - canvas.offsetLeft,
        y = e.pageY - canvas.offsetTop,
    
        /* Previous points: x-y */
        prevX = previousPoints[0],
        prevY = previousPoints[1];
    
        /* Drag direction! */
        positiveX = x > prevX, 
        positiveY = y > prevY, 
    
        /* The dragged distance! */
        value;

    /* This value will be used for x-coordinates */
    value = positiveX ? (x - prevX) : (prevX - x);
    if (positiveX) 
        circleX += value;
    else 
        circleX -= value;

    /* This value will be used for y-coordinates */
    value = positiveY ? (y - prevY) : (prevY - y);
    if (positiveY) 
        circleY += value;
    else 
        circleY -= value;

    /* Saving last x-y coordinates! - on mousemove */
    previousPoints = [x, y];
}

/* And to draw the circle! */
context.arc(circleX, circleY, 50, 0, Math.PI * 2, false);