# Rotate arrow on canvas

I need to draw a flow dynamically based on some user choices. In that flow I want to draw the choices (blue circles with number) and the directions of that choices (line and arrow). For example: node 1 to node 2.

JSFiddle Example

To draw the direction I draw a arrow in the end of the line but I can't make the arrow rotate around itself.

JS code

\$(document).ready(function () {
drawOnCanvas();
});

function drawOnCanvas() {
var canvas = document.getElementById('myCanvas');

if (canvas.getContext) {
var ctx = canvas.getContext("2d");

var circle1 = {
x: 75,
y: 75,
r: 15
};

var circle2 = {
x: 225,
y: 50,
r: 15
};

var arrow =
{
h: 5,
w: 10
};

drawCircle(ctx, circle1, "1");
drawCircle(ctx, circle2, "2");

var ptCircle1 = getPointOnCircle(circle1.r, circle1, circle2);
var ptCircle2 = getPointOnCircle(circle2.r, circle2, circle1);
var ptArrow = getPointOnCircle(circle2.r + arrow.w, circle2, circle1);

drawLine(ctx, ptCircle1, ptCircle2);
drawArrow(ctx, arrow, ptArrow, ptCircle2);
}
}

function drawArrow(canvasContext, arrow, ptArrow, endPt) {

var angleInDegrees = getAngleBetweenPoints(ptArrow, endPt);

canvasContext.beginPath();
// first save the untranslated/unrotated context
canvasContext.save();

// move the rotation point to the center of the rect
canvasContext.translate(ptArrow.x, ptArrow.y);
// rotate the rect
canvasContext.rotate(angleInDegrees);

canvasContext.moveTo(endPt.x, endPt.y);
canvasContext.lineTo(endPt.x - arrow.w, endPt.y + arrow.h);
canvasContext.lineTo(endPt.x - arrow.w, endPt.y - arrow.h);
canvasContext.closePath();
canvasContext.fillStyle = "rgb(72,72,72)";
canvasContext.stroke();
canvasContext.fill();

// restore the context to its untranslated/unrotated state
canvasContext.restore();
}

function drawCircle(canvasContext, circle, text) {
canvasContext.beginPath(); //começa ou reinicia o desenho de algo
canvasContext.fillStyle = "rgb(43,166,203)";
canvasContext.arc(circle.x, circle.y, circle.r, 0, 2 * Math.PI, false); //cria arcos
canvasContext.fill(); //atribui estilos

drawText(canvasContext, circle, text);
}

function drawText(canvasContext, circle, text) {
canvasContext.font = '8pt Calibri';
canvasContext.fillStyle = 'white';
canvasContext.textAlign = 'center';
canvasContext.fillText(text, circle.x, circle.y + 3);
}

function drawLine(canvasContext, startPt, endPt) {
canvasContext.moveTo(startPt.x, startPt.y);
canvasContext.lineTo(endPt.x, endPt.y);
canvasContext.stroke();
}

function getPointOnCircle(radius, originPt, endPt) {
var angleInDegrees = getAngleBetweenPoints(originPt, endPt);

// Convert from degrees to radians via multiplication by PI/180
var x = radius * Math.cos(angleInDegrees * Math.PI / 180) + originPt.x;
var y = radius * Math.sin(angleInDegrees * Math.PI / 180) + originPt.y;

return { x: x, y: y };
}

function getAngleBetweenPoints(originPt, endPt) {
var interPt = { x: endPt.x - originPt.x,
y: endPt.y - originPt.y
};

return Math.atan2(interPt.y, interPt.x) * 180 / Math.PI;
}


I think the problem is in drawArrow() method in the lines

canvasContext.translate(ptArrow.x, ptArrow.y);

canvasContext.rotate(angleInDegrees);


I already tried every possible values for the rotation and the translation but arrow still don't rotate properly around itself. Can anyone help me?

## 1 Answer

You got it wrong in your draw code : once you translated the context to a given point, you shouldn't use any more the point coordinates : the 'zero' is now at the translation point.
So to draw your arrow after you translated to its start point, just translate by the size of the arrow.

http://jsfiddle.net/gamealchemist/jxrdtLxx/2/

function drawArrow(canvasContext, arrow, ptArrow, endPt) {
var angleInDegrees = getAngleBetweenPoints(ptArrow, endPt);

// first save the untranslated/unrotated context
canvasContext.save();

// move the rotation point to the center of the rect
canvasContext.translate(ptArrow.x, ptArrow.y);
// rotate the rect
canvasContext.rotate(angleInDegrees*Math.PI/180);

canvasContext.beginPath();
canvasContext.moveTo(0,0);
canvasContext.lineTo( 0, -arrow.h);
canvasContext.lineTo( arrow.w, 0);
canvasContext.lineTo( 0, +arrow.h);
canvasContext.closePath();
canvasContext.fillStyle = "rgb(72,72,72)";
canvasContext.stroke();
canvasContext.fill();

// restore the context to its untranslated/unrotated state
canvasContext.restore();
}


Rq : i changed your css, since it was scaling the canvas. Change the size of the canvas directly in the canvas tag to avoid stretching artifacts.

• Fantastic! Great job! Thanks a lot. Can you explain me why circle 2 is with border? That isn't my intent.. Commented Jan 16, 2015 at 12:19
• You're welcome. For the circle border, it's the common mistake : you forgot to beginPath() before drawing (stroke()) the line. (jsfiddle.net/gamealchemist/jxrdtLxx/4). Yo ! Commented Jan 16, 2015 at 12:50