我在 a 中有一些二次贝塞尔曲线Canvas
。如果它们只有 1-2 px 宽,并且我想提供某种公差,那么我怎么能最好地hit detection
使用它们,这样用户就不必精确地点击就行了。
有什么东西可以计算到贝塞尔曲线的最小距离,如果这个距离足够小,选择贝塞尔曲线?
我在 a 中有一些二次贝塞尔曲线Canvas
。如果它们只有 1-2 px 宽,并且我想提供某种公差,那么我怎么能最好地hit detection
使用它们,这样用户就不必精确地点击就行了。
有什么东西可以计算到贝塞尔曲线的最小距离,如果这个距离足够小,选择贝塞尔曲线?
我可以想到至少 3 种方法来扩大二次贝塞尔曲线的命中区域
我不会推荐第一个解决方案,但无论如何都在这里!
解决方案#1-根据贝塞尔曲线上计算的各个点手动测试 clickPoint
这是一个计算 XY 的函数,它是沿着贝塞尔曲线的 n%,还有一个函数来测试您的 clickPoint 是否在该贝塞尔曲线点的范围内。
var startPt=makePt(10,100);
var controlPt=makePt(50,30);
var endPt=makePt(90,100);
function makePt(X,Y){ return( { x:X, y:Y } ) }
// find points at various percent along bezier path
// (where percent is a decimal from 0 to 1)
function getQuadraticBezierXY(percent,startPt,controlPt,endPt) {
var x = Math.pow(1-percent,2) * startPt.x + 2 * (1-percent) * percent * controlPt.x + Math.pow(percent,2) * endPt.x;
var y = Math.pow(1-percent,2) * startPt.y + 2 * (1-percent) * percent * controlPt.y + Math.pow(percent,2) * endPt.y;
return( makePt(x,y) );
}
// find whether 2 points are close to each other
// range is your pixel tolerance
function arePointsInRange(bezPt,testPt,range){
var dx=testPt.x-bezPt.x;
var dy=testPt.y-bezPt.y;
return( dx*dx+dy*dy <= range*range )
}
解决方案#2——针对“加宽”曲线的封闭路径进行命中测试
注意:下面使用的 isPointInPath() 在现代浏览器上可用,但在旧版浏览器上不可用
注意:您不必实际向用户显示加宽的曲线——您可以绘制加宽的曲线,但不能绘制 context.stroke()。(请务必查看 isPointInPath 的文档)。
注意:请务必调整起点和终点之间直线斜率的偏移量。为简单起见,我下面的插图使用 0 斜率。
这是代码和小提琴:http: //jsfiddle.net/m1erickson/4GEeu/
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
ctx.lineWidth=2;
ctx.strokeStyle="red";
var startX=10;
var startY=100;
var controlX=50;
var controlY=50;
var endX=90;
var endY=100;
var offset=20;
ctx.beginPath();
ctx.moveTo(startX,startY-offset);
ctx.quadraticCurveTo(controlX,controlY-offset,endX,endY-offset);
ctx.lineTo(endX,endY+offset);
ctx.quadraticCurveTo(controlX,controlY+offset,startX,startY+offset);
ctx.closePath();
ctx.stroke();
// hitTest point [15,110] which is known to be inside
// the widened curve path
if(ctx.isPointInPath(15,110)){
alert("Point [15,110] is in the closed quadratic curve path");
}
解决方案#3——在屏幕外画布上针对加宽曲线进行命中测试
注意:我的插图只是将屏幕上的曲线画得更宽。您可以在屏幕外画布上进行测试。
这是代码和小提琴:http: //jsfiddle.net/m1erickson/MJfZt/
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
ctx.lineWidth=20;
ctx.strokeStyle="red";
var startX=10;
var startY=100;
var controlX=50;
var controlY=50;
var endX=90;
var endY=100;
var offset=20;
ctx.beginPath();
ctx.moveTo(startX,startY);
ctx.quadraticCurveTo(controlX,controlY,endX,endY);
ctx.stroke();
// hitTest point [10,100] which is known to be inside
// the widened curve path
if(hittestByColor(10,100,255,0,0)){
alert("Pixel [10,100] is inside the widened curve");
}
function hittestByColor(x,y,red,green,blue){
var pxData = ctx.getImageData(x,y,1,1);
return(pxData.data[0]==red
&& pxData.data[1]==green
&& pxData.data[2]==blue);
}