Draw arrow on canvas tag

南笙酒味 提交于 2019-12-28 01:48:07

问题


I want to draw an arrow using the canvas tag, javascript. I've made it using the quadratic function, but I'm having problems to calculate the angle of rotation of the arrow...

Anyone have a clue on this?

Thank you


回答1:


As simple as I can get it. You'll have to prepend context.beginPath() and append context.stroke() yourself:

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>



回答2:


Ok, so the first answer on this page helped me greatly when I was trying to figure this problem out myself, although as someone else already stated, if you have a line width greater than 1px you get funny shapes. The fix that someone else suggested almost worked, but I still had some issues when trying to go for a thicker width arrow. After several hours of playing around with it I was able to combine the above solution with some of my own tinkering to come up with the following code that will draw an arrow at whatever thickness you desire without distorting the arrow shape.

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();
            }

This is now the code that I am using in my program. What I found to be the key with eliminating the distortion issue was continuing the stroke from the tip of the arrow to one side point, to the other side point, back to the tip, and back over to the first side point, then doing a fill. This corrected the shape of the arrow.

Hope this helps!




回答3:


Here is another method to draw arrows. It uses the triangle method from here: https://stackoverflow.com/a/8937325/1828637

A little helper function.

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();
}

And here is a demonstration of it to draw arrows at the start and at the end of a line.

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:


You can do:

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



回答5:


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>



回答6:


Given a size and the starting position, following code will draw the arrow for you.

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>



回答7:


This code is similar to Titus Cieslewski's solution, maybe the arrow is a bit nicer:

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))));
}

this works well with lineWidth > 1. It can come in handy when drawing x and y axis




回答8:


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);
  }
}



回答9:


You can push your matrix, rotate it, draw your arrow and then pop the matrix.




回答10:


I've been struggeling with this for quite some time now. I needed to to this in both javascript and c#. For javascript i found a nice library jCanvas.

My main problem was drawing nicely looking arrow heads, which jCanvas does perfectly. For my c# project i reverse engineered the jCanvas code.

Hopefully this helps somebody




回答11:


Here is the working solution

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();
}


来源:https://stackoverflow.com/questions/808826/draw-arrow-on-canvas-tag

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!