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>