Search code examples
javascripthtmldomdom-events

Does mousemove event act differently according to mouse direction?


I have the following piece of code

canvas1.addEventListener('mousedown', function(e) {
    var x = e.clientX - canvas1.getBoundingClientRect().left;
    var y = e.clientY - canvas1.getBoundingClientRect().top;

    if (rect.currentActiveBox == currentActiveBox) {
        // Incase we try to draw another box first clear out the previous box.
        console.log('clearing ', rect);
        ctx.clearRect(rect.startX - 6 , rect.startY - 6, rect.w + 8, rect.h + 8); 
    }

    rect.startX = x;
    rect.startY = y;

    isDrawing = true;
    rect.currentActiveBox = currentActiveBox;
    rects[currentActiveBox] = JSON.parse(JSON.stringify(rect));
});

canvas1.addEventListener('mousemove', function(e) {
    if(isDrawing === true) {
        // rect.w = (e.pageX - this.offsetLeft) - rect.startX;
        // rect.h = (e.pageY - this.offsetTop) - rect.startY ;
        rect.w = e.pageX - rect.startX;
        rect.h = e.pageY - rect.startY;
        ctx.clearRect(rect.startX, rect.startY, rect.w, rect.h);
        ctx.beginPath();
        ctx.strokeStyle = "red";
        ctx.lineWidth = 3;
        ctx.strokeRect(rect.startX, rect.startY, rect.w, rect.h);
        rects[currentActiveBox] = JSON.parse(JSON.stringify(rect));
    }
});

which just draws a box as shown below enter image description here

However this only works if I drag the mouse from top to bottom manner. If I try dragging the mouse from bottom up, I get ghost boxes as shown bellow.

enter image description here

I would expect the mousemove direction shouldn't change how the drawing happens. Is there something obvious that I might be missing?


Solution

  • There's a few things:

    • As ProfDFrancis has pointed out, your stroke actually extends outside the bounding box rectangle, so you need to expand your cleared area
    • You're clearing the new rectangle rather than the previous rectangle, so if the pointer movements are more than 1px at a time you will leave artifacts
    • You're being inconsistent with your pointer offsets – pageX and clientX are not interchangeable, so your commented-out calculation for rect.w and rect.h (which appears to take the element offset into consideration, as in the mousedown handler) is correct.

    You could do something like:

        if(isDrawing === true) {
            const strokeSize = 3;
            
            // Compute bounding box for all pixels touched by the stroke –
            // be sure to get a generous border to cope with antialiasing
            const clearLeft   = Math.min(rect.startX, rect.startX + rect.w) - strokeSize;
            const clearTop    = Math.min(rect.startY, rect.startY + rect.h) - strokeSize;
            const clearRight  = Math.max(rect.startX, rect.startX + rect.w) + strokeSize;
            const clearBottom = Math.max(rect.startY, rect.startY + rect.h) + strokeSize;
            ctx.clearRect(clearLeft, clearTop, clearRight - clearLeft, clearBottom - clearTop);
            
            rect.w = e.pageX - this.offsetLeft - rect.startX;
            rect.h = e.pageY - this.offsetTop - rect.startY;
    
            ctx.beginPath();
            // etc
        }
    

    fiddle