Robert Clouse
Robert Clouse

Reputation: 21

Using Angle in degrees to draw arrow in the direction in Javascript on Canvas

So I looked at the answer here: Draw arrow head in canvas using Angle

But it didn't seem to do it the way I wanted. I definitely do not want to use rotate, what I would like, is based on an Angle in degrees (0 being up on the screen, 180 being down, etc) is draw an arrow pointing in that direction.

Now I slept through trig in highschool so the correct usage of Rads, Sin and Cos are... well, they elude me :(.

Anyways, I have the angle already computed, and based on that I want to draw like the following:

enter image description here The top one is at 0 degrees in my computation, the lower one 90 degrees.

I'm using a 2d canvas as my draw surface.

Upvotes: 1

Views: 1625

Answers (2)

Blindman67
Blindman67

Reputation: 54026

Overkill maybe?

This may help or may not.

Rather than create the arrow (shape) with a set of draw calls you can create a Path2D to hold the shape with the helper function Path (in demo code below) that converts a set of arrays (each array a sub path) to a Path2D object. For example. const arrow = Path([[0, 0, 180, 0], [160, -10, 180, 0, 160, 10]]); a simple line arrow. To draw the path call ctx.stroke(arrow) or ctx.fill(arrow);

Define a style using Style eg const red = Style(...["#F00", , 2, "round"])

Position, rotate, and scale

Then the function drawPath(ctx, path, style, centerX, centerY, deg, scale) where...

  • ctx context to draw on.
  • path path to draw
  • style style to use,
  • centerX, centerY point to rotate around
  • deg angle in degrees from 0 deg pointing up, and positive values moving clockwize
  • scale lets you set the scale and defaults to 1.

Example use

// draw red arrow pointing up
const arrow = Path([[0, 0, 180, 0], [160, -10, 180, 0, 160, 10]]);
const red = Style(...["#F00", , 2, "round"]);
drawPath(ctx, arrow, red, 200, 200, 0);

Demo

Working example draw 6 arrows using 3 styles rotating around center of canvas.

const Path = (paths) => {
  var  j = 0, xx, yy, path = new Path2D();;
  for (const subPath of paths) {
    j = 0;
    while (j < subPath.length) {
      const [x, y] = [subPath[j++], subPath[j++]];
      j === 2 ?  
          path.moveTo(...([xx, yy] = [x, y])) :
          xx === x && yy === y ? path.closePath() : path.lineTo(x, y);
    }
  }
  return path;
}
const Style = (strokeStyle, fillStyle, lineWidth, lineJoin = "bevel") => ({strokeStyle, fillStyle, lineWidth, lineJoin});

const styles = {
    redLine:     Style(...["#F00", , 6, "round"]),
    greenFill:   Style(...[ , "#0A0"]),
    fillAndLine: Style(...["#000", "#0AF", 2, "round"]),
};
const paths = {
    lineArrow:    Path([ [10, 0, 180, 0], [160, -10, 180, 0, 160, 10] ]),
    fatArrow:     Path([ [10, -5, 180, -5, 170, -15, 200, 0, 170, 15, 180, 5, 10, 5, 10, -5] ]),
    diamondArrow: Path([ [60, 0, 100, -15, 150, 0, 100, 15, 60, 0] ]),
};

requestAnimationFrame(mainLoop);
const [W, H, ctx] = [can.width, can.height, can.getContext("2d")];
const DEG2RAD = Math.PI / 180, DEG_0_OFFSET = -90;

function drawPath(ctx, path, style, centerX, centerY, deg, scale = 1) {
     const rad = (deg + DEG_0_OFFSET) * DEG2RAD;
     const [ax, ay] = [Math.cos(rad) * scale, Math.sin(rad) * scale];
     ctx.setTransform(ax, ay, -ay, ax, centerX, centerY);
     Object.assign(ctx, style);
     style.fillStyle && ctx.fill(path);
     style.strokeStyle && ctx.stroke(path);
     ctx.setTransform(1, 0, 0, 1, 0, 0);
}
function mainLoop(time) {     
    ctx.clearRect(0, 0, W, H);
    drawPath(ctx, paths.fatArrow,     styles.fillAndLine, W * 0.5, H * 0.5, (time / 15000) * 360, 0.5);
    drawPath(ctx, paths.fatArrow,     styles.greenFill,   W * 0.5, H * 0.5, (time / 20000) * 360);
    drawPath(ctx, paths.diamondArrow, styles.fillAndLine, W * 0.5, H * 0.5, (time / 30000) * 360, 0.5);
    drawPath(ctx, paths.diamondArrow, styles.greenFill,   W * 0.5, H * 0.5, (time / 60000) * 360);
    drawPath(ctx, paths.lineArrow,    styles.redLine,     W * 0.5, H * 0.5, (time / 5000)  * 360, 0.9);
    drawPath(ctx, paths.lineArrow,    styles.redLine,     W * 0.5, H * 0.5, (time / 10000) * 360);
    requestAnimationFrame(mainLoop);
}
<canvas id="can" width="400" height="400"></canvas>

Upvotes: 0

IT goldman
IT goldman

Reputation: 19485

Inspired by the trigonometry of this answer I made a line + two smaller lines for the arrow part.

const size = 200;

var canvas = document.querySelector("canvas")
canvas.width = size;
canvas.height = size;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, size, size);
ctx.strokeStyle = "red"

function lineToAngle(ctx, x1, y1, length, angle) {
  angle = (angle - 90) * Math.PI / 180;
  var x2 = x1 + length * Math.cos(angle),
    y2 = y1 + length * Math.sin(angle);

  ctx.beginPath();
  ctx.moveTo(x1, y1);
  ctx.lineTo(x2, y2);
  ctx.stroke();
  ctx.fill();

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

function draw_arrow(ctx, x1, y1, length, angle) {
  var pos = lineToAngle(ctx, x1, y1, length, angle);
  lineToAngle(ctx, pos.x, pos.y, 10, angle - 135);
  lineToAngle(ctx, pos.x, pos.y, 10, angle + 135);
}

var pos = draw_arrow(ctx, 50, 50, 50, 30);

ctx.strokeStyle = "blue"
for (var angle = 0; angle <= 360; angle += 30) {
  draw_arrow(ctx, 100, 100, 60, angle);
}
<canvas></canvas>

Upvotes: 1

Related Questions