在画布标签上绘制箭头

IT技术 javascript html5-canvas drawing
2021-02-25 06:31:01

我想使用画布标签 javascript 绘制一个箭头。我已经使用二次函数做了它,但是我在计算箭头的旋转角度时遇到了问题......

任何人都有这方面的线索?

谢谢

6个回答

尽可能简单。您必须自己添加 context.beginPath() 并附加 context.stroke() :

ctx = document.getElementById("c").getContext("2d");
ctx.beginPath();
canvas_arrow(ctx, 10, 30, 200, 150);
canvas_arrow(ctx, 100, 200, 400, 50);
canvas_arrow(ctx, 200, 30, 10, 150);
canvas_arrow(ctx, 400, 200, 100, 50);
ctx.stroke();


function canvas_arrow(context, fromx, fromy, tox, toy) {
  var headlen = 10; // length of head in pixels
  var dx = tox - fromx;
  var dy = toy - fromy;
  var angle = Math.atan2(dy, dx);
  context.moveTo(fromx, fromy);
  context.lineTo(tox, toy);
  context.lineTo(tox - headlen * Math.cos(angle - Math.PI / 6), toy - headlen * Math.sin(angle - Math.PI / 6));
  context.moveTo(tox, toy);
  context.lineTo(tox - headlen * Math.cos(angle + Math.PI / 6), toy - headlen * Math.sin(angle + Math.PI / 6));
}
<html>

<body>
  <canvas id="c" width="500" height="500"></canvas>


</body>

这会产生一个奇怪的形状,你想摆脱最后一步并在最后添加 lineTo(tox,toy)
2021-04-19 06:31:01
@rakloscontext.lineCap = 'round'应该这样做。
2021-04-23 06:31:01
@danharper 图像是画布箭头吗?看起来很圆,如果是这样你怎么做?
2021-04-27 06:31:01
当 lineWidth 不是 == 1 时,函数无法正常工作
2021-04-29 06:31:01
对于更大的线宽,只需在context.moveTo(tox, toy);后面添加context.lineTo(tox, toy);(参见:i.imgur.com/jMOsLM9.png
2021-05-02 06:31:01

好的,所以当我试图自己解决这个问题时,这个页面上的第一个答案对我帮助很大,尽管正如其他人已经说过的那样,如果你的线宽大于 1px,你会得到有趣的形状。其他人建议的修复几乎有效,但在尝试使用更宽的箭头时我仍然遇到一些问题。经过几个小时的尝试,我能够将上述解决方案与我自己的一些修补结合起来,得出以下代码,该代码将绘制您想要的任何厚度的箭头,而不会扭曲箭头形状。

function drawArrow(fromx, fromy, tox, toy){
                //variables to be used when creating the arrow
                var c = document.getElementById("myCanvas");
                var ctx = c.getContext("2d");
                const width = 22;
                var headlen = 10;
                // This makes it so the end of the arrow head is located at tox, toy, don't ask where 1.15 comes from
                tox -= Math.cos(angle) * ((width*1.15));
                toy -= Math.sin(angle) * ((width*1.15));

                var angle = Math.atan2(toy-fromy,tox-fromx);
                
                //starting path of the arrow from the start square to the end square and drawing the stroke
                ctx.beginPath();
                ctx.moveTo(fromx, fromy);
                ctx.lineTo(tox, toy);
                ctx.strokeStyle = "#cc0000";
                ctx.lineWidth = width;
                ctx.stroke();
                
                //starting a new path from the head of the arrow to one of the sides of the point
                ctx.beginPath();
                ctx.moveTo(tox, toy);
                ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
                
                //path from the side point of the arrow, to the other side point
                ctx.lineTo(tox-headlen*Math.cos(angle+Math.PI/7),toy-headlen*Math.sin(angle+Math.PI/7));
                
                //path from the side point back to the tip of the arrow, and then again to the opposite side point
                ctx.lineTo(tox, toy);
                ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));

                //draws the paths created above
                ctx.strokeStyle = "#cc0000";
                ctx.lineWidth = width;
                ctx.stroke();
                ctx.fillStyle = "#cc0000";
                ctx.fill();
            }

现在这是我在我的程序中使用的代码。我发现消除失真问题的关键是继续从箭头尖端到一侧点,另一侧点,回到尖端,再回到第一个侧点,然后做一个充满。这修正了箭头的形状。

希望这可以帮助!

我从来没有用代码把它弄得一团糟。我基本上只是玩弄尺寸,直到我绘制箭头后看起来正确为止。对不起,我没有更好的答案给你。:\
2021-04-26 06:31:01
通过在 之后添加这两行来修改函数var angle = Math.atan2(toy-fromy,tox-fromx);tox -= Math.cos(angle) * ((width*1.15)); toy -= Math.sin(angle) * ((width*1.15));这将使箭头以 tox 和 toy 结尾。@CodingbyRaj
2021-04-30 06:31:01
我无法在解决方案中找到箭头的确切点。你有什么想法吗?
2021-05-14 06:31:01

这是绘制箭头的另一种方法。它使用这里的三角形方法:https : //stackoverflow.com/a/8937325/1828637

一个小助手功能。

function canvas_arrow(context, fromx, fromy, tox, toy, r){
    var x_center = tox;
    var y_center = toy;

    var angle;
    var x;
    var y;

    context.beginPath();

    angle = Math.atan2(toy-fromy,tox-fromx)
    x = r*Math.cos(angle) + x_center;
    y = r*Math.sin(angle) + y_center;

    context.moveTo(x, y);

    angle += (1/3)*(2*Math.PI)
    x = r*Math.cos(angle) + x_center;
    y = r*Math.sin(angle) + y_center;

    context.lineTo(x, y);

    angle += (1/3)*(2*Math.PI)
    x = r*Math.cos(angle) + x_center;
    y = r*Math.sin(angle) + y_center;

    context.lineTo(x, y);

    context.closePath();

    context.fill();
}

这是在一行的开头和结尾绘制箭头的演示。

var can = document.getElementById('c');
var ctx = can.getContext('2d');

ctx.lineWidth = 10;
ctx.strokeStyle = 'steelblue';
ctx.fillStyle = 'steelbllue'; // for the triangle fill
ctx.lineJoin = 'butt';

ctx.beginPath();
ctx.moveTo(50, 50);
ctx.lineTo(150, 150);
ctx.stroke();

canvas_arrow(ctx, 50, 50, 150, 150, 10);
canvas_arrow(ctx, 150, 150, 50, 50, 10);

function canvas_arrow(context, fromx, fromy, tox, toy, r){
	var x_center = tox;
	var y_center = toy;
	
	var angle;
	var x;
	var y;
	
	context.beginPath();
	
	angle = Math.atan2(toy-fromy,tox-fromx)
	x = r*Math.cos(angle) + x_center;
	y = r*Math.sin(angle) + y_center;

	context.moveTo(x, y);
	
	angle += (1/3)*(2*Math.PI)
	x = r*Math.cos(angle) + x_center;
	y = r*Math.sin(angle) + y_center;
	
	context.lineTo(x, y);
	
	angle += (1/3)*(2*Math.PI)
	x = r*Math.cos(angle) + x_center;
	y = r*Math.sin(angle) + y_center;
	
	context.lineTo(x, y);
	
	context.closePath();
	
	context.fill();
}
<canvas id="c" width=300 height=300></canvas>

为什么要使用 (1/3)*(2*Math.PI) 而不仅仅是 Math.PI / 1.5?它给出了完全相同的结果,但操作更少。
2021-04-20 06:31:01
尖端是偏移的(它以线端为中心,而不是完全指向它),但x_center -= r * Math.cos(angle); y_center -= r * Math.sin(angle);在第一个角度计算后添加可以解决这个问题。
2021-05-12 06:31:01

你可以做:

ctx.save();
ctx.translate(xOrigin, yOrigin);
ctx.rotate(angle);
 // draw your arrow, with its origin at [0, 0]
ctx.restore();

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

ctx.clearRect(0, 0, canvas.width, canvas.height);	
arrow({x: 10, y: 10}, {x: 100, y: 170}, 10);
arrow({x: 40, y: 250}, {x: 10, y: 70}, 5);


function arrow (p1, p2, size) {
  var angle = Math.atan2((p2.y - p1.y) , (p2.x - p1.x));
  var hyp = Math.sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y));

  ctx.save();
  ctx.translate(p1.x, p1.y);
  ctx.rotate(angle);

  // line
  ctx.beginPath();	
  ctx.moveTo(0, 0);
  ctx.lineTo(hyp - size, 0);
  ctx.stroke();

  // triangle
  ctx.fillStyle = 'blue';
  ctx.beginPath();
  ctx.lineTo(hyp - size, size);
  ctx.lineTo(hyp, 0);
  ctx.lineTo(hyp - size, -size);
  ctx.fill();

  ctx.restore();
}
<canvas id = "canvas" width = "300" height = "400"></canvas>

谢谢先生,您帮我解决了我所需要的问题
2021-04-17 06:31:01