画布确定点是否在线上



我正在尝试设计一个折线图,当用户悬停在一条线上时,它将显示一个工具提示。下面是一个最小的例子,显示了我试图确定一个点是否位于一条线上(在这种情况下,该线可以被视为高度为5、宽度为80的矩形(。

我不明白为什么isPointInPath只能找到直线起点和终点的点。如何确定一个点是否位于直线上的任何位置?

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.lineWidth = 5;
ctx.moveTo(20, 20);
ctx.lineTo(100, 20);
ctx.stroke();
console.log(ctx.isPointInPath(20, 20)); // beginning: true
console.log(ctx.isPointInPath(100, 20)); // end: true
console.log(ctx.isPointInPath(60, 20)); // middle: false
console.log(ctx.isPointInPath(100, 21)); // end offset: false

isPointInPath确实使用填充区域进行检查。你想要isPointInStroke(),它也考虑了lineWidth

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const path = new Path2D();
let x = -1;
let y = -1;
for (let i = 0; i<20; i++) {
path.lineTo(
Math.random() * canvas.width, 
Math.random() * canvas.height
);
}
ctx.lineWidth = 5;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = ctx.isPointInStroke(path, x, y)
? "red"
: "green";
ctx.stroke(path);
}
draw();
addEventListener("mousemove", (evt) => {
const rect = canvas.getBoundingClientRect();
x = evt.clientX - rect.left;
y = evt.clientY - rect.top;
draw();
});
<canvas></canvas>

至于为什么isPointInPath()会找到在Path声明中明确设置的点,而不是逻辑上位于其上的点……恐怕这还不清楚,规范没有明确的fill算法,我将打开一个规范问题来解决这个问题,因为Safari实际上存在互操作问题,正如你所期望的那样,Firefox+Chrome在不产生像素时完全忽略填充区域,但仍保留这些点。

如果你想要一个函数,告诉你一个点是否在一条宽度为5px的线上,我们应该使用一些分析几何:一个点与一条线的距离。我从维基百科复制了公式

function isPointOnLine(px, py, x1, y1, x2, y2, width) {
return distancePointFromLine(px, py, x1, y1, x2, y2, width) <= width / 2
}
function distancePointFromLine(x0, y0, x1, y1, x2, y2) {
return Math.abs((x2 - x1) * (y1 - y0) - (x1 - x0) * (y2 - y1)) / Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
}

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
ctx.lineWidth = 5;
x1 = y1 = y2 = 20
x2 = 100
ctx.moveTo(20, 20);
ctx.lineTo(100, 20);
ctx.stroke();
console.log(isPointOnLine(20, 20, x1, y1, x2, y2, 5)); 
console.log(isPointOnLine(100, 20, x1, y1, x2, y2, 5)); 
console.log(isPointOnLine(60, 20, x1, y1, x2, y2, 5)); 
console.log(isPointOnLine(100, 21, x1, y1, x2, y2, 5));
<canvas></canvas>

相关内容

最新更新