在HTML5 Canvas上绘制两个对象之间的箭头
Draw an arrow on HTML5 Canvas between two objects
我正在制作概念地图应用程序,它有一组节点和链接。我使用节点的中心作为参考将链接连接到节点。由于我有不同大小和形状的节点,因此不建议通过指定形状的高度或宽度来为链接绘制箭头。我的方法是画一个链接,从一个节点开始,像素一个像素,直到到达下一个节点(这里的节点与背景的颜色不同),然后通过访问像素值,我希望能够确定链接和节点的交点,这实际上是绘制箭头的坐标。
如果我能得到一些帮助,那就太好了。
示例代码:http://jsfiddle.net/9tUQP/4/
这里绿色的方块是节点,从左边方块开始进入右边方块的线是链接。我想把箭头画在链接和右方块的交点上
我已经创建了一个这样做的示例。我使用Bresenham的直线算法来遍历整个画布像素的直线,并检查每个点的alpha值;每当它越过一个"阈值"点,我就把它记录为一个候选点。然后,我使用第一个和最后一个这样的点来绘制一个箭头(带有适当旋转的箭头)。
示例如下:http://phrogz.net/tmp/canvas_shape_edge_arrows.html
刷新示例以查看新的随机测试用例。如果你有另一个"形状"已经重叠了其中一个端点,它就会"失败"。解决这个问题的一种方法是先在空白画布上绘制形状,然后将结果(drawImage
)复制到最终画布上。
对于Stack Overflow后代(以防我的网站崩溃),这里是相关代码:
<!DOCTYPE html>
<html><head>
<meta charset="utf-8">
<title>HTML5 Canvas Shape Edge Detection (for Arrow)</title>
<style type="text/css">
body { background:#eee; margin:2em 4em; text-align:center; }
canvas { background:#fff; border:1px solid #666 }
</style>
</head><body>
<canvas width="800" height="600"></canvas>
<script type="text/javascript">
var ctx = document.querySelector('canvas').getContext('2d');
for (var i=0;i<20;++i) randomCircle(ctx,'#999');
var start = randomDiamond(ctx,'#060');
var end = randomDiamond(ctx,'#600');
ctx.lineWidth = 2;
ctx.fillStyle = ctx.strokeStyle = '#099';
arrow(ctx,start,end,10);
function arrow(ctx,p1,p2,size){
ctx.save();
var points = edges(ctx,p1,p2);
if (points.length < 2) return
p1 = points[0], p2=points[points.length-1];
// Rotate the context to point along the path
var dx = p2.x-p1.x, dy=p2.y-p1.y, len=Math.sqrt(dx*dx+dy*dy);
ctx.translate(p2.x,p2.y);
ctx.rotate(Math.atan2(dy,dx));
// line
ctx.lineCap = 'round';
ctx.beginPath();
ctx.moveTo(0,0);
ctx.lineTo(-len,0);
ctx.closePath();
ctx.stroke();
// arrowhead
ctx.beginPath();
ctx.moveTo(0,0);
ctx.lineTo(-size,-size);
ctx.lineTo(-size, size);
ctx.closePath();
ctx.fill();
ctx.restore();
}
// Find all transparent/opaque transitions between two points
// Uses http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
function edges(ctx,p1,p2,cutoff){
if (!cutoff) cutoff = 220; // alpha threshold
var dx = Math.abs(p2.x - p1.x), dy = Math.abs(p2.y - p1.y),
sx = p2.x > p1.x ? 1 : -1, sy = p2.y > p1.y ? 1 : -1;
var x0 = Math.min(p1.x,p2.x), y0=Math.min(p1.y,p2.y);
var pixels = ctx.getImageData(x0,y0,dx+1,dy+1).data;
var hits=[], over=null;
for (x=p1.x,y=p1.y,e=dx-dy; x!=p2.x||y!=p2.y;){
var alpha = pixels[((y-y0)*(dx+1)+x-x0)*4 + 3];
if (over!=null && (over ? alpha<cutoff : alpha>=cutoff)){
hits.push({x:x,y:y});
}
var e2 = 2*e;
if (e2 > -dy){ e-=dy; x+=sx }
if (e2 < dx){ e+=dx; y+=sy }
over = alpha>=cutoff;
}
return hits;
}
function randomDiamond(ctx,color){
var x = Math.round(Math.random()*(ctx.canvas.width - 100) + 50),
y = Math.round(Math.random()*(ctx.canvas.height - 100) + 50);
ctx.save();
ctx.fillStyle = color;
ctx.translate(x,y);
ctx.rotate(Math.random() * Math.PI);
var scale = Math.random()*0.8 + 0.4;
ctx.scale(scale,scale);
ctx.lineWidth = 5/scale;
ctx.fillRect(-50,-50,100,100);
ctx.strokeRect(-50,-50,100,100);
ctx.restore();
return {x:x,y:y};
}
function randomCircle(ctx,color){
ctx.save();
ctx.beginPath();
ctx.arc(
Math.round(Math.random()*(ctx.canvas.width - 100) + 50),
Math.round(Math.random()*(ctx.canvas.height - 100) + 50),
Math.random()*20 + 10,
0, Math.PI * 2, false
);
ctx.fillStyle = color;
ctx.fill();
ctx.lineWidth = 2;
ctx.stroke();
ctx.restore();
}
</script>
</body></html>
相关文章:
- javascript函数,它接受两个输入:一个对象和一个键,并返回对象中该键的相应值
- 正在更新mongod中两个对象内部的数组
- 添加两个对象以获取值的总和
- 如何使用javascript合并两个对象数组
- 别名或以其他方式合并两个具有不同名称的相同对象原型
- 如何对两个嵌套对象进行排序
- 在JavaScript中相等两个对象.在更改一时,秒会自动更改
- js字符串转换为具有两个值的对象
- 如何使用AngularJs比较两个对象的JSON
- jQuery对象从html表中查询为两个一维数组,用于Chartist图表
- 交换对象上两个属性的值
- Backbone relational无法实例化两个RelationalModel对象
- 如何在javascript中合并两个对象数组
- 删除两个独立对象中的属性,而不使用多余的删除语句
- JavaScript - 合并两个对象数组并根据属性值删除重复数据
- 有可能让两个变量指向同一个对象吗?(javascript)
- 使用Javascript对象为具有两个背景图像的DIV设置样式时出现问题
- 如何在Javascript中连接两个结构不同的对象
- 如何使用jQuery合并两个json对象
- 如何按对象两个值对对象数组进行排序