javascripthtmlcanvasscalingcursor-position

Cursor not align using HTML canvas


I have the following Javascript, HTML and CSS code

function distanceBetween(point1, point2) {
    return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2));
}

function angleBetween(point1, point2) {
    return Math.atan2(point2.x - point1.x, point2.y - point1.y);
}

function draw() {
    var img = new Image();
    img.src = 'http://www.tricedesigns.com/wp-content/uploads/2012/01/brush2.png';

    var el = document.getElementById('DrawingPattern');
    var ctx = el.getContext('2d');
    ctx.lineJoin = ctx.lineCap = 'round';

    var isDrawing, lastPoint;

    el.onmousedown = function (e) {
        isDrawing = true;
        lastPoint = {x: e.clientX, y: e.clientY};
    };

    el.onmousemove = function (e) {
        if (!isDrawing) return;

        var currentPoint = {x: e.clientX, y: e.clientY};
        var dist = distanceBetween(lastPoint, currentPoint);
        var angle = angleBetween(lastPoint, currentPoint);

        for (var i = 0; i < dist; i++) {
            x = lastPoint.x + (Math.sin(angle) * i) - 25;
            y = lastPoint.y + (Math.cos(angle) * i) - 25;
            ctx.drawImage(img, x, y);
        }

        lastPoint = currentPoint;
    };

    el.onmouseup = function () {
        isDrawing = false;
    };
}
html, body {
    color: #fff;
    background: #000;
    font-family: "Lucida Grande";
}

canvas{
    position: static;
    cursor: move;
    margin: auto;
    width: 70%;
    border: 3px solid #73AD21;
    background: white;
    overflow: visible;
}
<body onload=draw()>
<canvas width="1024" height="649" id="DrawingPattern">
<!-- add fallback content-->
</canvas>
</body>

The whole code works, the only problem that I'm having is when I start drawing on the canvas, the cursor doesn't line up with the effective position of the cursor. So when I draw it does the lines in another position, higher or lower than the actual position of the cursor is. I've tried different ways but, I guess is some sort of scaling problem but I'm not able how to fix it.


Solution

  • It is a scaling problem. Even though you scale the canvas element by setting CSS width by percentage, the size of the drawing area doesn't change. You get the mouse coordinates in screen space, but you need to transform into canvas space when drawing.

    To do this, calculate the scale by taking into account the difference between el.width (canvas width) and el.offsetWidth (element width). As the canvas is scaled proportionately by keeping the aspect ratio, the same scaling can be used for both the X and Y coordinates.

    I have added an elementScale() function and updated the code to multiply clientX and clientY with the return value.

    function distanceBetween(point1, point2) {
        return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2));
    }
    
    function angleBetween(point1, point2) {
        return Math.atan2(point2.x - point1.x, point2.y - point1.y);
    }
    
    function elementScale(el) {
        return el.offsetWidth === 0 ? 0 : (el.width / el.offsetWidth);
    }
    
    function draw() {
        var img = new Image();
        img.src = 'http://www.tricedesigns.com/wp-content/uploads/2012/01/brush2.png';
    
        var el = document.getElementById('DrawingPattern');
        var ctx = el.getContext('2d');
        ctx.lineJoin = ctx.lineCap = 'round';
    
        var isDrawing, lastPoint;
    
        el.onmousedown = function (e) {
            var scale = elementScale(el);
            isDrawing = true;
            lastPoint = {x: e.clientX * scale, y: e.clientY * scale};
        };
    
        el.onmousemove = function (e) {
            if (!isDrawing) return;
    
            var scale = elementScale(el);
            var currentPoint = {x: e.clientX * scale, y: e.clientY * scale};
            var dist = distanceBetween(lastPoint, currentPoint);
            var angle = angleBetween(lastPoint, currentPoint);
    
            for (var i = 0; i < dist; i++) {
                x = lastPoint.x + (Math.sin(angle) * i) - 25;
                y = lastPoint.y + (Math.cos(angle) * i) - 25;
                ctx.drawImage(img, x, y);
            }
    
            lastPoint = currentPoint;
        };
    
        el.onmouseup = function () {
            isDrawing = false;
        };
    }
    html, body {
        color: #fff;
        background: #000;
        font-family: "Lucida Grande";
    }
    
    canvas{
        position: static;
        cursor: move;
        margin: auto;
        width: 70%;
        border: 3px solid #73AD21;
        background: white;
        overflow: visible;
    }
    <body onload=draw()>
    <canvas width="1024" height="649" id="DrawingPattern">
    <!-- add fallback content-->
    </canvas>
    </body>