如何在一行中填充封闭的形状?

时间:2016-05-09 23:46:13

标签: javascript canvas

我使用了html画布创建了这一行:

  

a squiggly line, which loops over itself in some places to create enclosed circle and balloon shapes

我想要填充行中的循环,以便它看起来像这样:

  

the enclosed shapes are filled in with red

然而,当我填写它时,它只是变成:

  

the beginning and end of the line have been invisibly connected, and the whole area filled in, enclosed shapes included

我确实尝试使用路径,这是完全相同的结果,只是连接开始到结束的一条线。

代码抽象:

var canvas = $("canvas")[0], ctx=canvas.getContext("2d");
ctx.moveto(0,0);
// code to stroke path of mouse cursor;

如何获得我想要的结果并在包含

中包含的形状?

2 个答案:

答案 0 :(得分:4)

问题是fill()方法正在关闭路径,基本上是从起点到终点绘制一条线。结果就是如你所见,整个路径都已填满。

一种可能的解决方案,尽管对于看似随机的线路来说很难,但是将指针返回到fill()不会导致不必要的填充的位置。以下示例演示了这一点。绘制线后,我只需将指针返回到closePath()不会导致任何封闭区域填充的位置。

右侧的画布将最终点移动到中间位置,fill()表现得符合要求。

var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');

context.beginPath();
context.moveTo(100, 20);
context.lineTo(100, 120);
context.lineTo(150, 120);
context.lineTo(150, 70);
context.lineTo(50, 70);

context.fill();

context.lineWidth = 2;
context.strokeStyle = 'blue';
context.stroke();

var canvas = document.getElementById('myCanvas2');
var context = canvas.getContext('2d');

context.beginPath();
context.moveTo(100, 20);
context.lineTo(100, 120);
context.lineTo(150, 120);
context.lineTo(150, 70);
context.lineTo(50, 70);

// Go back to a position the line
// won't cause unwanted fills
context.lineTo(100, 70);

context.fill();

context.lineWidth = 2;
context.strokeStyle = 'blue';
context.stroke();
<canvas id="myCanvas" width="250" height="250"></canvas>
<canvas id="myCanvas2" width="250" height="250"></canvas>
  

答案 1 :(得分:3)

<强>更新

问题在于Brett指出fill()将隐含地关闭路径。使用API​​我们无能为力,因此我们需要使用手动方法将循环填充为单独的封闭路径。

寻找十字路口

此算法执行以下操作(尚未检查这些循环可能重叠的情况,但它应该让您开始)。它也可以在移动鼠标时实时重写。

  • 我们可以迭代路径中的所有段。将每个段与所有其他段进行比较,但是从当前段的两个点开始,否则当前段的终点将与下一段的起点自相交。
  • 如果找到了一个十字路口:
    • 从交叉点构建路径,然后使用第一条和最后一条相交线之间的线段添加点

实施例

result

&#13;
&#13;
var points = [49,40,49,41,49,42,49,43,49,45,49,48,49,50,49,53,49,56,49,59,49,63,49,67,49,72,50,77,51,82,53,88,53,91,55,96,58,99,60,104,62,106,64,109,65,113,68,116,70,118,72,120,74,121,76,124,78,125,81,126,87,129,92,130,98,133,104,134,109,135,113,135,117,135,121,135,127,135,131,135,135,135,141,132,148,128,153,126,159,122,161,120,164,118,164,116,165,112,165,110,165,107,165,105,165,104,165,101,165,100,164,96,163,94,162,93,160,91,159,90,158,88,157,88,156,88,154,88,151,88,147,88,141,90,135,92,130,94,126,96,121,99,118,101,114,104,111,108,108,110,107,113,104,117,103,120,100,125,99,129,96,135,95,139,95,144,95,148,95,152,95,155,95,158,96,162,97,166,99,170,102,173,106,177,109,181,111,182,113,184,115,185,117,186,119,186,121,186,124,186,127,186,132,185,135,183,141,179,146,175,152,172,158,168,165,165,172,162,178,159,185,158,191,157,195,156,199,156,202,156,206,156,209,156,212,157,216,160,220,163,221,168,224,170,224,173,225,177,227,182,228,186,229,192,229,197,230,203,230,208,230,212,230,219,230,225,230,230,228,236,226,240,221,246,217,251,214,255,210,257,204,260,199,260,194,261,189,261,184,261,181,261,177,261,175,261,173,260,173,256,171,252,170,245,170,237,169,231,168,226,168,221,168,218,168,215,168,212,168,211,169,207,172,205,175,201,180,199,187,198,194,196,201,194,208,194,214,194,221,194,225,194,230,195,235,196,240,199,245,202,247,204,251,207,253,210,254,214,255,216,259,223,263,229,266,235,270,241,273,245,277,253,279,257,283,262,287,269,292,274,297,280,302,285,308,290,314,294,321,295,327,296,336,298,343,298,352,298,359,298,367,292,374,286,379,278,381,269,381,262,381,254,381,246,381,241,379,232,377,229,372,224,369,221,364,219,361,219,355,218,347,218,339,218,330,218,320,221,310,228,300,235,290,242,282,249,276,257,271,263,269,269,267,276,266,281,266,287,266,291,267,297,272,305,279,312,286,319,296,327,305,332,316,338,325,341,333,344,340,348,342,348,344,349,345,349,345,350,346,351,347,353,347,355,347,356,347,358,347,361,347,363,347,366,347,370,347,374,344,379,343,384,342,393,339,400,335,406,331,414,323,421,317,426,310,430,302,435,295,437],
    ctx = c.getContext("2d"),
    i, y, ip, t, l = points.length;

// compare each segments
for(i = 0; i < points.length - 4; i += 2) {
  for(y = i + 4; y < points.length - 2; y += 2) {
    
    ip = intersection(points[i], points[i+1], points[i+2], points[i+3],
                      points[y], points[y+1], points[y+2], points[y+3]);
    
    // any intersction? create a sub-path with segments between the intersecting lines
    if (ip) {
      ctx.moveTo(ip.x, ip.y);
      for(t = i + 2; t < y; t += 2) ctx.lineTo(points[t], points[t+1]);
    }
  } 
}

// fill all sub-paths at once
ctx.fillStyle = "red";
ctx.fill();

// stroke path itself
ctx.beginPath();
ctx.moveTo(points[0], points[1]);
for(i = 0; i < l; i += 2) ctx.lineTo(points[i], points[i+1]);
ctx.stroke();

function intersection(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) {

  var d1x = p1x - p0x, d1y = p1y - p0y,
      d2x = p3x - p2x, d2y = p3y - p2y,
      d = d1x * d2y - d2x * d1y,
      px, py, s, t;

    if (d) {
      px = p0x - p2x;
      py = p0y - p2y;
      s = (d1x * py - d1y * px) / d;
      if (s >= 0 && s <= 1) {
        t = (d2x * py - d2y * px) / d;
        if (t >= 0 && t <= 1) return {x: p0x + (t * d1x), y: p0y + (t * d1y)};
      }
    }
    return null
}
&#13;
<canvas id=c width=500 height=500></canvas>
&#13;
&#13;
&#13;