keldar
keldar

Reputation: 6252

HTML5 Canvas - draw circle from top left

How would I alter the following code to force a HTML5 canvas to draw from the top-left of an arc, instead of from the centre.

I currently have:

function drawCircle(e) {
    var oe = e.originalEvent || e;

    ctx.beginPath();
    ctx.arc(start.x, start.y + 10, oe.pageX - start.x - offset.left, 0, 2 * Math.PI, false);
    ctx.stroke();
    ctx.closePath();
}

I have the following fiddle (code below for convenience) - if you begin drawing on that, the circle is drawn fine but it draws from the centre of where you place your mouse, outwards: http://jsfiddle.net/g4zxp/

I would like to drag the circle from the top-left down, but I cannot figure out the maths to do it! I know I would need to calculate and keep changing the centre point.

Any ideas?

Thanks

Code as follows:

HTML:

<canvas width="200" height="200"></canvas>

JavaScript/jQuery:

$(function () {
    var $canvas = $('canvas'),
        canvas = $canvas.get(0),
        offset = $canvas.offset(),
        ctx,
        drawing = false,
        start = {
            x: null,
            y: null
        };

    function init() {
        if (canvas.getContext) {
            ctx = canvas.getContext('2d');

            ctx.lineJoin = 'round';
            ctx.lineCap = 'round';
            ctx.strokeStyle = '#000000';
            ctx.lineWidth = 5;
        }
    }

    function startDrawing(e) {
        var oe = e.originalEvent || e;

        e.preventDefault();

        drawing = true;

        start.x = Math.floor(oe.pageX - offset.left);
        start.y = Math.floor(oe.pageX - offset.left);
    }

    function continueDrawing(e) {
        e.preventDefault();

        if (!drawing) {
            return;
        }

        clearCanvas();
        drawCircle(e);
    }

    function endDrawing(e) {
        e.preventDefault();
        drawing = false;
    }

    function clearCanvas() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
    }

    function drawCircle(e) {
        var oe = e.originalEvent || e;

        ctx.beginPath();
        ctx.arc(start.x, start.y + 10, oe.pageX - start.x - offset.left, 0, 2 * Math.PI, false);
        ctx.stroke();
        ctx.closePath();
    }

    $canvas.on('touchstart mousedown', function (e) {
        startDrawing(e);
    });

    $canvas.on('touchmove mousemove', function (e) {
        continueDrawing(e);
    });

    $canvas.on('touchend mouseup', function (e) {
        endDrawing(e);
    });

    init();
});

Upvotes: 1

Views: 2759

Answers (2)

WebWanderer
WebWanderer

Reputation: 10897

Ok, so there is an insanely simple way to do this, and you only had an error in your ctx.arc() call, thats all:

ctx.arc(oe.layerX, oe.layerY, Math.abs(oe.pageX - start.x - offset.left), 0, 2 * Math.PI, false);

I also set your radius to an absolute value so that you can draw the circle in the negative direction without crashing your app. Wish I would've gotten to you sooner, I figured you would have wanted this very simple answer before.

By the way, here is your fiddle all fixed up and doing exactly what you wanted it to do. Sorry I didn't get to you sooner!

Upvotes: 1

Zach Saucier
Zach Saucier

Reputation: 26014

I am assuming the "top left of an arc" to be the intersection of the lines in the diagram below. In that case, the distance from the center of the circle is a distance of sqrt(2 * r^2) away from the "top left"

But, since circles in canvas are positioned using x and y anyway, we can use that instead. The distance from the center to the closest point on the vertical line is r, as is the distance from the center to the nearest point on the horizontal line

This means that (since (0,0) is in the top left) the "top left's" x position is center's x position - r and the "top left's" y position is center's y position - r or (cX - r, cY - r) for short

You can manually calculate this in canvas using your code like (since oe.pageX - start.x - offset.left = r) so:

var r = oe.pageX - start.x - offset.left;
ctx.arc(start.x + r, start.y + 10 + r, r, 0, 2 * Math.PI, false);

Demo. This works just fine.

However you could make a function if you want to use the same format on the front end. It'd look something like this

function arcTopLeft(x, y, r, a1, a2) {
    ctx.arc(x + r, y + r, r, a1, a2, false);
}
// Called using the same inputs as your original call
arcTopLeft(start.x, start.y + 10, oe.pageX - start.x - offset.left, 0, 2 * Math.PI, false);

Hope it helped!

Upvotes: 3

Related Questions