Tôi đã tạo một ví dụ thực hiện việc này. Tôi sử dụng Bresenham's Line Algorithm để đi bộ đường thẳng của toàn bộ pixel pixel và kiểm tra alpha tại mỗi điểm; bất cứ khi nào nó đi qua một điểm 'ngưỡng' tôi ghi lại đó là một ứng cử viên. Sau đó tôi sử dụng điểm đầu tiên và cuối cùng để vẽ mũi tên (với đầu mũi tên xoay đúng).
Đây là ví dụ: http://phrogz.net/tmp/canvas_shape_edge_arrows.html
Làm mới ví dụ để xem trường hợp thử nghiệm ngẫu nhiên mới. Nó 'thất bại' nếu bạn có một 'hình dạng' khác chồng lên nhau một trong những điểm kết thúc. Một cách để giải quyết vấn đề này là vẽ hình dạng của bạn trước tiên lên một khung trống và sau đó sao chép kết quả (drawImage
) vào canvas cuối cùng.
Đối với Stack Overflow hậu thế (trong trường hợp trang web của tôi là xuống) đây là đoạn code liên quan:
<!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>
liên kết đến công việc của tôi: http://jsfiddle.net/emYhJ/ Bạn có thể xin refactor nó để phù hợp với ma cua toi. –
Cảm ơn anyway .. Đã tìm ra :) –
@ allwyn.menezes tại sao bạn muốn vẽ nó qua khối lập phương thứ 2? Bạn có thể cập nhật fiddle của bạn? – FutuToad