Javascript 围绕直线创建多边形

Javascript 围绕直线创建多边形,javascript,math,canvas,html5-canvas,drawing,Javascript,Math,Canvas,Html5 Canvas,Drawing,我有一条线,表示为X,Y坐标的数组。我通过HTML5画布将其显示在屏幕上,并希望提供用户交互。为此,我需要查看用户鼠标是否在线,以提供视觉反馈,并允许他们移动鼠标等 该线显示为“线”,笔划赋予其厚度,因此仅检查鼠标是否“在”该线不会很好地工作,因为用户很难准确地越过该线 出于这个原因,我想在直线周围创建一个多边形(实质上是添加填充)。这意味着用户不必直接在线,只要非常靠近它。然后我将使用这个多边形进行命中测试 如何将点列表(我的线)转化为一个用填充表示该线的多边形?(比如说10px) 我想在直线

我有一条线,表示为X,Y坐标的数组。我通过HTML5画布将其显示在屏幕上,并希望提供用户交互。为此,我需要查看用户鼠标是否在线,以提供视觉反馈,并允许他们移动鼠标等

该线显示为“线”,笔划赋予其厚度,因此仅检查鼠标是否“在”该线不会很好地工作,因为用户很难准确地越过该线

出于这个原因,我想在直线周围创建一个多边形(实质上是添加填充)。这意味着用户不必直接在线,只要非常靠近它。然后我将使用这个多边形进行命中测试

如何将点列表(我的线)转化为一个用填充表示该线的多边形?(比如说10px)

我想在直线周围创建一个多边形(基本上是添加 填充)。这意味着用户不必直接登录到 线,离它很近。然后我会用这个多边形 命中测试

您不需要通过数学来实现这一点,只需使用内置的
isPointInStroke()
并预先设置
线宽
lineCap
,以增加“灵敏度”(只需将其用于
isPointInStroke()
用于使用IE的用户,或采用更难的方法,如f.exby@Mbo中的方法)

您可以将路径存储为对象并使用这些对象进行命中测试,也可以构建当前路径并为其设置
lineWidth
,以进行测试。请注意,如果不是当前路径,则需要重建要测试的路径

例子
var ctx=c.getContext(“2d”),
点数=[
{x:10,y:120},
{x:110,y:20},
{x:310,y:20},
{x:410,y:120}
];
//创建当前路径并绘制多段线
创建路径(点);
ctx.stroke();
//增加“填充”,对于演示,显示区域
ctx.lineWidth=20;//要评估的填充区域
ctx.lineCap=“圆形”//线帽,包括评估
ctx.strokeStyle=“rgba(200,0,0,0.2)”;//不需要,仅用于演示
ctx.stroke();
//感应鼠标
c、 onmousemove=函数(e){
var r=this.getBoundingClientRect(),
x=e.clientX-r.left,
y=e.clientY-r.top;
info.innerHTML=ctx.isPointInStroke(x,y)?“点击”:“外部”;
};
//构建路径
函数createPath(点){
ctx.beginPath();
移动到(点[0].x,点[0].y);
对于(变量i=1,p;p=points[i++];)ctx.lineTo(p.x,p.y);
}

我想在直线周围创建一个多边形(基本上是添加 填充)。这意味着用户不必直接登录到 线,离它很近。然后我会用这个多边形 命中测试

您不需要通过数学来实现这一点,只需使用内置的
isPointInStroke()
并预先设置
线宽
lineCap
,以增加“灵敏度”(只需将其用于
isPointInStroke()
用于使用IE的用户,或采用更难的方法,如f.exby@Mbo中的方法)

您可以将路径存储为对象并使用这些对象进行命中测试,也可以构建当前路径并为其设置
lineWidth
,以进行测试。请注意,如果不是当前路径,则需要重建要测试的路径

例子
var ctx=c.getContext(“2d”),
点数=[
{x:10,y:120},
{x:110,y:20},
{x:310,y:20},
{x:410,y:120}
];
//创建当前路径并绘制多段线
创建路径(点);
ctx.stroke();
//增加“填充”,对于演示,显示区域
ctx.lineWidth=20;//要评估的填充区域
ctx.lineCap=“圆形”//线帽,包括评估
ctx.strokeStyle=“rgba(200,0,0,0.2)”;//不需要,仅用于演示
ctx.stroke();
//感应鼠标
c、 onmousemove=函数(e){
var r=this.getBoundingClientRect(),
x=e.clientX-r.left,
y=e.clientY-r.top;
info.innerHTML=ctx.isPointInStroke(x,y)?“点击”:“外部”;
};
//构建路径
函数createPath(点){
ctx.beginPath();
移动到(点[0].x,点[0].y);
对于(变量i=1,p;p=points[i++];)ctx.lineTo(p.x,p.y);
}

选项#1:您可以围绕线绘制多边形,使其成为“胖目标”

选项2:您可以使用
isPointInStroke
点击测试笔划

选项#3:纯数学的选择。

Math具有跨浏览器兼容的优势(
isPointInStroke
在IE/Edge上失败)

以下是如何……

计算鼠标到直线上最近点的距离

// find XY on line closest to mouse XY
// line shape: {x0:,y0:,x1:,y1:}
// mouse position: mx,my
function closestXY(line,mx,my){
    var x0=line.x0;
    var y0=line.y0;
    var x1=line.x1;
    var y1=line.y1;
    var dx=x1-x0;
    var dy=y1-y0;
    var t=((mx-x0)*dx+(my-y0)*dy)/(dx*dx+dy*dy);
    t=Math.max(0,Math.min(1,t));
    var x=lerp(x0,x1,t);
    var y=lerp(y0,y1,t);
    return({x:x,y:y});
}

// linear interpolation -- needed in closestXY()
function lerp(a,b,x){return(a+x*(b-a));}
如果该距离在您的10px“命中范围”内,则选择该线

// is the mouse within 10px of the line
var hitTolerance=10;
var dx=mx-closestPt.x;
var dy=my-closestPt.y;
var distance=Math.sqrt(dx*dx+dy*dy);
if(distance<=hitTolerance){
    // this line is w/in 10px of the mouse
}

用鼠标拖动线条。
必须在线条的10px范围内开始拖动
选项#1:您可以围绕线绘制多边形,使其成为“胖目标”

选项2:您可以使用
isPointInStroke
点击测试笔划

选项#3:纯数学的选择。

Math具有跨浏览器兼容的优势(
isPointInStroke
在IE/Edge上失败)

以下是如何……

计算鼠标到直线上最近点的距离

// find XY on line closest to mouse XY
// line shape: {x0:,y0:,x1:,y1:}
// mouse position: mx,my
function closestXY(line,mx,my){
    var x0=line.x0;
    var y0=line.y0;
    var x1=line.x1;
    var y1=line.y1;
    var dx=x1-x0;
    var dy=y1-y0;
    var t=((mx-x0)*dx+(my-y0)*dy)/(dx*dx+dy*dy);
    t=Math.max(0,Math.min(1,t));
    var x=lerp(x0,x1,t);
    var y=lerp(y0,y1,t);
    return({x:x,y:y});
}

// linear interpolation -- needed in closestXY()
function lerp(a,b,x){return(a+x*(b-a));}
如果该距离在您的10px“命中范围”内,则选择该线

// is the mouse within 10px of the line
var hitTolerance=10;
var dx=mx-closestPt.x;
var dy=my-closestPt.y;
var distance=Math.sqrt(dx*dx+dy*dy);
if(distance<=hitTolerance){
    // this line is w/in 10px of the mouse
}

用鼠标拖动线条。
必须在线条的10px范围内开始拖动
附近的直线、线段和点。 对于所涉及的数学问题,有两个函数可以提供帮助

点到线的距离。

以下函数用于查找点到线的距离

// return distance of point (px,py) to line ((l1x,l1y),(l2x,l2y))
distPoint2Line = function(px, py, l1x, l1y, l2x, l2y){
    var v1x,v1y,v2x,v2y,l,c;
    v1x = l2x - l1x;  // convert the line to a vector basicly moves the line 
    v1y = l2y - l1y;  // so that it starts at 0,0
    v2x = px - l1x;  // shift the point the same distance so it is in the 
    v2y = py - l1y;  // same relative position
    // Useful math trick 
    // The following finds the unit length of the closest point 
    // on the line vector V1 to the point v2
    // u is unbounded and can have any value but if it is 
    // 0 <= u <= 1 then that point is on the line where 
    // where u = 0 is the start u = 0.5 the middle and u = 1 the end
    // u < 0 is before the line start and u > 1 is after the line end
    // in math gargon. Get the dot product of V2 . V1 divided by the length squared of V1
    u = (v2x * v1x + v2y * v1y)/(v1x * v1x + v1y * v1y);
    // Now if we multiply the vector of the line V1 by c we get the
    // coordinates of the closest point on the line
    v1x *= u;
    v1y *= u;
    // now it is simple to find the distance from that point on the 
    // line to the point via pythagoras
    v1x -= v2x;  // distance between the two points
    v1y -= v2y;
    // sqrt of the sum of the square of the sides 
    return Math.sqrt(v1x * v1x + v1y * v1y);
}
什么时候线不是线?

在某些情况下,两点(有效数字)所描述的直线不是我们可以处理的直线。长度为零的线
   u = (v1x * v1x + v2x * v2x);
   u = u === 0 ? 0 : (v2x * v1x + v2y * v1y) / u;   // if u is 0 then set it as 0 
var lineHelper = (function(){ // call it what you want
    var hypot = Math.hypot;
    if(typeof hypot !== "function"){ // poly fill for hypot
         hypot = function(x,y){
            return Math.sqrt(x * x + y * y);
         }
    }
    var lenSq, unitDist, minDist, v1x, v1y, v2x, v2y, lsx, lsy, vx,vy; // closure vars
    var dP2L = function(px, py, l1x, l1y, l2x, l2y){
        v1x = l2x - l1x; 
        v1y = l2y - l1y;  
        v2x = px - (lsx = l1x);  
        v2y = py - (lsy = l1y);  
        unitDist = (v1x * v1x + v1y * v1y);
        unitDist = unitDist === 0 ? 0 : (v2x * v1x + v2y * v1y) / unitDist;
        return hypot((v1x *= unitDist) - v2x, (v1y *= unitDist) - v2y);
    }
    var dP2LS = function(px, py, l1x, l1y, l2x, l2y){
        v1x = l2x - l1x; 
        v1y = l2y - l1y;  
        v2x = px - (lsx = l1x);  
        v2y = py - (lsy = l1y);  
        unitDist = (v1x * v1x + v1y * v1y);
        unitDist = unitDist === 0 ? 0 : Math.max(0, Math.min(1, (v2x * v1x + v2y * v1y) / unitDist));
        return hypot((v1x *= unitDist) - v2x, (v1y *= unitDist) - v2y);
    }
    var dP2V = function(px, py, l1x, l1y){  // point dist to vector
        unitDist = (l1x * l1x + l1y * l1y);
        unitDist = unitDist === 0 ? 0 : unitDist = Math.max(0, Math.min(1, (px * l1x + py * l1y) / unitDist));
        return hypot((v1x = l1x * unitDist) - px, (v1y = l1y * unitDist) - py);
    }
    var cLineSeg = function(px, py, array, closed){
         var i, len, leni, dist, lineIndex;
         minDist = Infinity;
         leni = len = array.length;
         if(! closed){
            leni -= 2;
         }
         for(i = 0; i < leni; i += 2){
            dist = dP2V(px - array[i], py - array[i + 1], array[(i + 2) % len] - array[i], array[(i + 3) % len] - array[i +1]);
            if(dist < minDist){
                lineIndex = i;
                minDist = dist;
                lsx = array[i];
                lsy = array[i + 1];
                vx = v1x;
                vy = v1y;
            }
         }
         v1x = vx;
         v1y = vy;
         return lineIndex;
    }
    return {
        distPoint2Line : dP2L,
        distPoint2LineSeg : dP2LS,
        indexOfLineClosest2Point : cLineSeg,
        getPointOnLine : function(){ return [lsx + v1x,lsy + v1y] },
        getUnitDist : function() { return unitDist; },
        getMinDist : function() { return minDist; },
   }
})();