在画布标签上绘制箭头

时间:2009-04-30 20:18:03

标签: javascript drawing canvas

我想使用canvas标签javascript绘制箭头。我使用二次函数制作了它,但是我在计算箭头的旋转角度时遇到了问题......

有人对此有所了解吗?

谢谢

13 个答案:

答案 0 :(得分:77)

就像我能得到它一样简单。您必须自己添加context.beginPath()并自己附加context.stroke():

function canvas_arrow(context, fromx, fromy, tox, toy){
    var headlen = 10;   // length of head in pixels
    var angle = Math.atan2(toy-fromy,tox-fromx);
    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));
}

以下是一个示例: http://stuff.titus-c.ch/arrow.html

答案 1 :(得分:25)

好的,所以当我试图自己解决这个问题时,这页上的第一个答案对我有很大的帮助,尽管正如其他人已经说过的那样,如果你的线宽大于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");
                var headlen = 10;

                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 = 22;
                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 = 22;
                ctx.stroke();
                ctx.fillStyle = "#cc0000";
                ctx.fill();
            }

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

希望这有帮助!

答案 2 :(得分:7)

你可以这样做:

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

答案 3 :(得分:5)

这是绘制箭头的另一种方法。它使用此处的三角形方法: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>

答案 4 :(得分:3)

根据大小和起始位置,下面的代码将为您绘制箭头。

function draw_arrow(context, startX, startY, size) {
  var arrowX = startX + 0.75 * size;
  var arrowTopY = startY - 0.707 * (0.25 * size);
  var arrowBottomY = startY + 0.707 * (0.25 * size);
  context.moveTo(startX, startY);
  context.lineTo(startX + size, startX);
  context.lineTo(arrowX, arrowTopY);
  context.moveTo(startX + size, startX);
  context.lineTo(arrowX, arrowBottomY);
  context.stroke();
}
window.onload = function() {
  var canvas = document.getElementById("myCanvas");
  var context = canvas.getContext("2d");
  var startX = 50;
  var startY = 50;
  var size = 100;
  context.lineWidth = 2;
  draw_arrow(context, startX, startY, size);
};
body {
  margin: 0px;
  padding: 0px;
}

#myCanvas {
  border: 1px solid #9C9898;
}
<!DOCTYPE HTML>
<html>

<body onmousedown="return false;">
  <canvas id="myCanvas" width="578" height="200"></canvas>
</body>

</html>

答案 5 :(得分:2)

此代码类似于Titus Cieslewski的解决方案,也许箭头更好一点:

function canvasDrawArrow(context, fromx, fromy, tox, toy) {
    var headlen = 10.0;
    var back = 4.0;
    var angle1 = Math.PI / 13.0;
    var angle2 = Math.atan2(toy - fromy, tox - fromx);
    var diff1 = angle2 - angle1;
    var diff2 = angle2 + angle1;
    var xx = getBack(back, fromx, fromy, tox, toy);
    var yy = getBack(back, fromy, fromx, toy, tox);

    context.moveTo(fromx, fromy);
    context.lineTo(tox, toy);

    context.moveTo(xx, yy);
    context.lineTo(xx - headlen * Math.cos(diff1), yy - headlen * Math.sin(diff1));

    context.moveTo(xx, yy);
    context.lineTo(xx - headlen * Math.cos(diff2), yy - headlen * Math.sin(diff2));
}

function getBack(len, x1, y1, x2, y2) {
    return x2 - (len * (x2 - x1) / (Math.sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2))));
}

这适用于lineWidth > 1。绘制xy

时,它会派上用场

答案 6 :(得分:2)

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>

答案 7 :(得分:1)

function RTEShape()
{   
    this.x = 50;
  this.y = 50;
  this.w = 100; // default width and height?
  this.h = 100;
  this.fill = '#444444';
  this.text = "Test String";
  this.type;
  this.color;
  this.size = 6;    

    // The selection color and width. Right now we have a red selection with a small width
    this.mySelColor = '#CC0000';
    this.mySelWidth = 2;
    this.mySelBoxColor = 'darkred';// New for selection boxes
    this.mySelBoxSize = 6;
}

RTEShape.prototype.buildArrow = function(canvas)
{
    this.type = "arrow";

  // Make sure we don't execute when canvas isn't supported
  if (canvas.getContext){

    // use getContext to use the canvas for drawing
    var ctx = canvas.getContext('2d');           

    var oneThirdX = this.x + (this.w/3);             
    var twoThirdX = this.x + ((this.w*2)/3);

    var oneFifthY = this.y - (this.y/5);    
    var twoFifthY = this.y - ((this.y*3)/5);

    /**/
    //ctx.beginPath();
    ctx.moveTo(oneThirdX,this.y); // 125,125
    ctx.lineTo(oneThirdX,oneFifthY); // 125,105

    ctx.lineTo(this.x*2,oneFifthY); // 225,105      
    ctx.lineTo(this.x*2,twoFifthY); // 225,65

    ctx.lineTo(oneThirdX,twoFifthY); // 125,65      
    ctx.lineTo(oneThirdX,(this.y/5)); // 125,45

    ctx.lineTo(this.x,(this.y+(this.y/5))/2); // 45,85

        ctx.fillStyle = "green";
    ctx.fill();

    ctx.fillStyle = "yellow";
    ctx.fillRect(this.x,this.y,this.w,this.h);

  } else {
    alert('Error on buildArrow!\n'+err.description);
  }
}

答案 8 :(得分:0)

您可以推动矩阵,旋转矩阵,绘制箭头,然后弹出矩阵。

答案 9 :(得分:0)

我已经为此努力了很长时间。 我需要在javascript和c#中都做到这一点。对于javascript,我发现了一个不错的库jCanvas

我的主要问题是绘制漂亮的箭头,jCanvas可以完美地做到这一点。 对于我的C#项目,我反向工程了jCanvas代码。

希望这对某人有帮助

答案 10 :(得分:0)

这是可行的解决方案

function draw_arrow(ctx,fx,fy,tx,ty){ //ctx is the context
    var angle=Math.atan2(ty-fy,tx-fx);
    ctx.moveTo(fx,fy); ctx.lineTo(tx,ty);
    var w=3.5; //width of arrow to one side. 7 pixels wide arrow is pretty
    ctx.strokeStyle="#4d4d4d"; ctx.fillStyle="#4d4d4d";
    angle=angle+Math.PI/2; tx=tx+w*Math.cos(angle); ty=ty+w*Math.sin(angle);
    ctx.lineTo(tx,ty);
  //Drawing an isosceles triangle of sides proportional to 2:7:2
    angle=angle-1.849096; tx=tx+w*3.5*Math.cos(angle); ty=ty+w*3.5*Math.sin(angle);
    ctx.lineTo(tx,ty);
    angle=angle-2.584993; tx=tx+w*3.5*Math.cos(angle); ty=ty+w*3.5*Math.sin(angle);
    ctx.lineTo(tx,ty);
    angle=angle-1.849096; tx=tx+w*Math.cos(angle); ty=ty+w*Math.sin(angle);
    ctx.lineTo(tx,ty);
    ctx.stroke(); ctx.fill();
}

答案 11 :(得分:0)

打字稿版本,线宽>> 1时带有固定的箭头

function canvas_arrow( context, fromx, fromy, tox, toy ) {
    const dx = tox - fromx;
    const dy = toy - fromy;
    const headlen = Math.sqrt( dx * dx + dy * dy ) * 0.3; // length of head in pixels
    const angle = Math.atan2( dy, dx );
    context.beginPath();
    context.moveTo( fromx, fromy );
    context.lineTo( tox, toy );
    context.stroke();
    context.beginPath();
    context.moveTo( tox - headlen * Math.cos( angle - Math.PI / 6 ), toy - headlen * Math.sin( angle - Math.PI / 6 ) );
    context.lineTo( tox, toy );
    context.lineTo( tox - headlen * Math.cos( angle + Math.PI / 6 ), toy - headlen * Math.sin( angle + Math.PI / 6 ) );
    context.stroke();
}

enter image description here

答案 12 :(得分:0)

您好,非常感谢您的建议。

我可以建议你放弃笨重的 atan 吗?您也可以使用线性代数来添加或减去角度:

var cospix=0.866025404; //cosinus of pi/6

function canvas_arrow(context, fromx, fromy, tox, toy) {
ctx.strokeStyle = '#AA0000';
var headlen = 10; // length of head in pixels
var dx = tox - fromx;
var dy = toy - fromy;
var length = Math.sqrt(dy*dy + dx*dx); //length of arrow
var sina = dy/length, cosa = dx/length; //computing sin and cos of arrow angle
var cosp=cosa*cospix-0.5*sina, cosm=cosa*cospix+0.5*sina,
sinp=cosa*0.5+cospix*sina, sinm=cospix*sina-cosa*0.5;
//computing cos and sin of arrow angle plus pi/6, respectively minus pi/6
//(p for plus, m for minus at the end of variable's names)
context.moveTo(fromx, fromy);
context.lineTo(tox, toy);
context.lineTo(tox - headlen * cosm, toy - headlen * sinm); //computing coordinates using the cos and sin computed above
context.moveTo(tox, toy);
context.lineTo(tox - headlen * cosp, toy - headlen * sinp); //computing coordinates using the cos and sin computed above
}