我正在使用 wPaint 插件,并尝试添加更多功能。我需要的是一条以“箭头”结尾的画线。我已经尝试了几乎所有我能想到的东西,但我只能得到一半的箭头(想象一下<-----, but the head only extends to the bottom or the top, but never both directions.)
这是绘制线条的函数(带有半箭头):
drawArrowMove: function(e, _self)
{
var xo = _self.canvasTempLeftOriginal;
var yo = _self.canvasTempTopOriginal;
if(e.pageX < xo) { e.x = e.x + e.w; e.w = e.w * -1}
if(e.pageY < yo) { e.y = e.y + e.h; e.h = e.h * -1}
_self.ctxTemp.lineJoin = "round";
_self.ctxTemp.beginPath();
_self.ctxTemp.moveTo(e.x, e.y);
_self.ctxTemp.lineTo(e.x + e.w, e.y + e.h);
_self.ctxTemp.closePath();
_self.ctxTemp.moveTo(e.x, e.y);
_self.ctxTemp.lineTo(15,10);
_self.ctxTemp.stroke();
}
任何帮助/想法/提示都会有帮助。
谢谢。
这是创建一个在两端绘制箭头的线对象的方法
有趣的部分是计算箭头的角度,如下所示:
var startRadians=Math.atan((this.y2-this.y1)/(this.x2-this.x1));
startRadians+=((this.x2>=this.x1)?-90:90)*Math.PI/180;
var endRadians=Math.atan((this.y2-this.y1)/(this.x2-this.x1));
endRadians+=((this.x2>=this.x1)?90:-90)*Math.PI/180;
剩下的只是画线和 2 个三角形作为箭头来计算旋转
Line.prototype.drawArrowhead=function(ctx,x,y,radians){
ctx.save();
ctx.beginPath();
ctx.translate(x,y);
ctx.rotate(radians);
ctx.moveTo(0,0);
ctx.lineTo(5,20);
ctx.lineTo(-5,20);
ctx.closePath();
ctx.restore();
ctx.fill();
}
这里是代码和小提琴:http://jsfiddle.net/m1erickson/Sg7EZ/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var context=canvas.getContext("2d");
function Line(x1,y1,x2,y2){
this.x1=x1;
this.y1=y1;
this.x2=x2;
this.y2=y2;
}
Line.prototype.drawWithArrowheads=function(ctx){
// arbitrary styling
ctx.strokeStyle="blue";
ctx.fillStyle="blue";
ctx.lineWidth=1;
// draw the line
ctx.beginPath();
ctx.moveTo(this.x1,this.y1);
ctx.lineTo(this.x2,this.y2);
ctx.stroke();
// draw the starting arrowhead
var startRadians=Math.atan((this.y2-this.y1)/(this.x2-this.x1));
startRadians+=((this.x2>this.x1)?-90:90)*Math.PI/180;
this.drawArrowhead(ctx,this.x1,this.y1,startRadians);
// draw the ending arrowhead
var endRadians=Math.atan((this.y2-this.y1)/(this.x2-this.x1));
endRadians+=((this.x2>this.x1)?90:-90)*Math.PI/180;
this.drawArrowhead(ctx,this.x2,this.y2,endRadians);
}
Line.prototype.drawArrowhead=function(ctx,x,y,radians){
ctx.save();
ctx.beginPath();
ctx.translate(x,y);
ctx.rotate(radians);
ctx.moveTo(0,0);
ctx.lineTo(5,20);
ctx.lineTo(-5,20);
ctx.closePath();
ctx.restore();
ctx.fill();
}
// create a new line object
var line=new Line(50,50,150,150);
// draw the line
line.drawWithArrowheads(context);
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
这对于垂直线来说是错误的。 尝试一下
var line=new Line(50,50,50,275)
作为 markE 的答案与 user1707810 评论的补充:
两个块(开始/结束弧度):
- ((this.x2 > this.x1)?-90:90)*Math.PI/180;
应改为:
- ((this.x2 >= this.x1)?-90:90)*Math.PI/180;
更简单的版本
关键区别。使用
Math.atan2
就不再需要 if
这个也将箭头放在线的末端而不是超过线的末端
换句话说
这个
start end
|<------->|
与这个
<|---------|>
function arrow(ctx, x1, y1, x2, y2, start, end) {
var rot = -Math.atan2(x1 - x2, y1 - y2);
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
if (start) {
arrowHead(x1, y1, rot);
}
if (end) {
arrowHead(x2, y2, rot + Math.PI);
}
}
function arrowHead(x, y, rot) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(rot);
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(-5, -12);
ctx.lineTo(5, -12);
ctx.closePath();
ctx.fill();
ctx.restore();
}
// test it -------
var ctx = document.createElement("canvas").getContext("2d");
document.body.appendChild(ctx.canvas);
// draw some arrows
ctx.save();
ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
for (var ii = 0; ii <= 12; ++ii) {
var u = ii / 12;
var color = hsl(u * 360, 1, 0.5);
ctx.fillStyle = color;
ctx.strokeStyle = color;
var a = u * Math.PI;
var x = Math.cos(a) * 120;
var y = Math.sin(a) * 70;
arrow(ctx, -x, -y, x, y, true, true);
// draw the end points to see the arrowheads match
ctx.fillStyle = "#000";
ctx.fillRect(-x - 1, -y - 1, 3, 3);
ctx.fillRect( x - 1, y - 1, 3, 3);
}
ctx.restore();
function hsl(h, s, l) {
return `hsl(${h},${s * 100}%,${l * 100}%)`;
}
canvas { border: 1px solid black; }
上述解决方案的一个问题是,如果您加粗线条笔画,它会刺穿箭头。这并不难修复,但您必须计算线条的长度(以像素为单位),然后从两侧减去箭头的大小。
类似这样的事情
function arrow(ctx, x1, y1, x2, y2, start, end) {
var dx = x2 - x1;
var dy = y2 - y1;
var rot = -Math.atan2(dx, dy);
var len = Math.sqrt(dx * dx + dy * dy);
var arrowHeadLen = 10;
ctx.save();
ctx.translate(x1, y1);
ctx.rotate(rot);
ctx.beginPath();
ctx.moveTo(0, start ? arrowHeadLen : 0);
ctx.lineTo(0, len - (end ? arrowHeadLen : 0));
ctx.stroke();
if (end) {
ctx.save();
ctx.translate(0, len);
arrowHead(ctx);
ctx.restore();
}
if (start) {
ctx.rotate(Math.PI);
arrowHead(ctx);
}
ctx.restore();
}
function arrowHead(ctx) {
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(-5, -12);
ctx.lineTo(5, -12);
ctx.closePath();
ctx.fill();
}
// test it -------
var ctx = document.createElement("canvas").getContext("2d");
document.body.appendChild(ctx.canvas);
// draw some arrows
ctx.save();
ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
for (var ii = 0; ii < 12; ++ii) {
var u = ii / 12;
var color = hsl(u * 360, 1, 0.5);
ctx.fillStyle = color;
ctx.strokeStyle = color;
var a = u * Math.PI;
var x = Math.cos(a) * 120;
var y = Math.sin(a) * 70;
arrow(ctx, -x, -y, x, y, true, true);
ctx.fillStyle = "#000"; // mark the ends so we can see where they are
ctx.fillRect(-x - 1, -y - 1, 3, 3);
ctx.fillRect( x - 1, y - 1, 3, 3);
}
ctx.restore();
function hsl(h, s, l) {
return `hsl(${h},${s * 100}%,${l * 100}%)`;
}
canvas { border: 1px solid black; }
换句话说,第一个解决方案画的是这样的箭头
第二个解决方案画出这样的箭头
我的简单解决方案
ctx.beginPath();
ctx.moveTo(ax,ay);
ctx.lineTo(bx,by);
ctx.stroke();
ctx.closePath();
angle=Math.PI+Math.atan2(by-ay,bx-ax);
angle1=angle+Math.PI/6;
angle2=angle-Math.PI/6;
ctx.beginPath();
ctx.moveTo(bx,by);
ctx.arc(bx,by,5,angle1,angle2,true);
ctx.lineTo(bx,by);
ctx.fill();
ctx.closePath();
这是线条粗细的修复
具有偏移调整的相同解决方案。
<!doctype html>
<html>
<head>
<style>
body {
background-color: ivory;
}
canvas {
border: 1px solid red;
}
</style>
<script src="http://code.jquery.com/jquery.min.js"></script>
<script>
$(function () {
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
function Line(x1, y1, x2, y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
Line.prototype.drawWithArrowheads = function (ctx) {
// Styling
ctx.strokeStyle = "blue";
ctx.fillStyle = "blue";
ctx.lineWidth = 20; // Line thickness
// Draw the main line
ctx.beginPath();
ctx.moveTo(this.x1, this.y1);
ctx.lineTo(this.x2, this.y2);
ctx.stroke();
// Calculate angles for arrowheads
var angle = Math.atan2(this.y2 - this.y1, this.x2 - this.x1);
var arrowLength = 30; // Length of the arrowhead
var arrowWidth = 15; // Half the width of the arrowhead
var offset = ctx.lineWidth; // Offset based on line thickness
// Move the arrows outside the line
var adjustedX1 = this.x1 - Math.cos(angle) * offset;
var adjustedY1 = this.y1 - Math.sin(angle) * offset;
var adjustedX2 = this.x2 + Math.cos(angle) * offset;
var adjustedY2 = this.y2 + Math.sin(angle) * offset;
// Draw the ending arrowhead
this.drawArrowhead(ctx, adjustedX2, adjustedY2, angle, arrowLength, arrowWidth);
// Draw the starting arrowhead
this.drawArrowhead(ctx, adjustedX1, adjustedY1, angle + Math.PI, arrowLength, arrowWidth);
};
Line.prototype.drawArrowhead = function (ctx, x, y, angle, length, width) {
ctx.save();
ctx.translate(x, y); // Use adjusted positions
ctx.rotate(angle);
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(-length, -width);
ctx.lineTo(-length, width);
ctx.closePath();
ctx.fill();
ctx.restore();
};
// Create and draw the line
var line = new Line(50, 50, 150, 150);
line.drawWithArrowheads(context);
});
</script>
</head>
<body>
<canvas id="canvas" width="300" height="300"></canvas>
</body>
</html>