填充颜​​色的空心形状

时间:2014-04-29 17:39:26

标签: javascript html5 canvas

说我画了一个圆圈或任何有边界的形状,在其中是html canvas中的空心空间。我想用选定的颜色填充它。什么算法可以为我做的工作。我添加了一张图片一个空心的形状,我想填补黑色边界内的白色空间。这可以使用javascript.how鼠标事件lyk

e.layerX/Y ;e.ClientX/Y 

可以使用

here is my drawing shape.i want to fill the space within it with color

2 个答案:

答案 0 :(得分:1)

您可以使用Flood fill填充该地区。它需要一个起点(或种子点)作为输入,并通过尝试填充其邻居来递归填充该区域。

您可以在鼠标单击事件上获取种子点,然后将其传递给洪水填充。 这是JavaScript中基于堆栈的代码:

// Takes the seed point as input
var floodfill = function(point) {
    var stack = Array();
    stack.push(point); // Push the seed
    while(stack.length > 0) {
        var currPt = stack.pop();
        if(isEmpty(currPt.x, currPt.y)) { // Check if the point is not filled
            setPixel(currPt.x, currPt.y); // Fill the point
            stack.push(currPt.x + 1, currPt.y); // Fill the east neighbour
            stack.push(currPt.x, currPt.y + 1); // Fill the south neighbour
            stack.push(currPt.x - 1, currPt.y); // Fill the west neighbour
            stack.push(currPt.x, currPt.y - 1); // Fill the north neighbour
        }
    }
};

isEmpty(x, y)是测试点(x, y)是否为空的函数。

setPixel(x, y)填写了(x, y)点。

这些功能的实现由您自己完成。

请注意,上述算法使用了4-neighborhood。但它很容易扩展到8邻域。

答案 1 :(得分:1)

这是基于William Malone教程的演示:http://jsfiddle.net/m1erickson/67xaB/

<!DOCTYPE html>
<html>
  <head>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
    canvas{border:1px solid red;}
</style>

<script>
$(function(){

    //  The floodFill algorithm below is based on the good work by William Malone, Copyright 2010 William Malone (www.williammalone.com) -- Apache License: http://www.apache.org/licenses/LICENSE-2.0 -- Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

    var canvas=document.getElementById("canvas");
    var context = canvas.getContext("2d");
    var $canvas=$("#canvas");
    var canvasOffset=$canvas.offset();
    var offsetX=canvasOffset.left;
    var offsetY=canvasOffset.top;
    var canvasWidth = canvas.width;
    var canvasHeight = canvas.height;
    var strokeColor =  {r: 0, g: 0, b: 0};
    var fillColor =  {r: 101,g: 155,b: 65};
    var fillData;
    var strokeData;


    // load image
    var img=new Image();
    img.onload=function(){
        start();
    }
    img.crossOrigin="anonymous";
    img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/polygrid.png";


    function matchstrokeColor(r, g, b, a) {
      // never recolor the initial black divider strokes
      // must check for near black because of anti-aliasing
      return (r + g + b < 100 && a === 255);  
    }

    function matchStartColor(pixelPos, startR, startG, startB) {

      // get the color to be matched
      var r = strokeData.data[pixelPos],
        g = strokeData.data[pixelPos + 1],
        b = strokeData.data[pixelPos + 2],
        a = strokeData.data[pixelPos + 3];

      // If current pixel of the outline image is black-ish
      if (matchstrokeColor(r, g, b, a)) {
        return false;
      }

      // get the potential replacement color
      r = fillData.data[pixelPos];
      g = fillData.data[pixelPos + 1];
      b = fillData.data[pixelPos + 2];

      // If the current pixel matches the clicked color
      if (r === startR && g === startG && b === startB) {
        return true;
      }

      // If current pixel matches the new color
      if (r === fillColor.r && g === fillColor.g && b === fillColor.b) {
        return false;
      }

      return true;
    }

    // Thank you William Malone!
    function floodFill(startX, startY, startR, startG, startB) {
      var newPos;
      var x;
      var y;
      var   pixelPos;
      var   neighborLeft;
      var   neighborRight;
      var   pixelStack = [[startX, startY]];

      while (pixelStack.length) {

        newPos = pixelStack.pop();
        x = newPos[0];
        y = newPos[1];

        // Get current pixel position
        pixelPos = (y * canvasWidth + x) * 4;

        // Go up as long as the color matches and are inside the canvas
        while (y >= 0 && matchStartColor(pixelPos, startR, startG, startB)) {
          y -= 1;
          pixelPos -= canvasWidth * 4;
        }

        pixelPos += canvasWidth * 4;
        y += 1;
        neighborLeft = false;
        neighborRight = false;

        // Go down as long as the color matches and in inside the canvas
        while (y <= (canvasHeight-1) && matchStartColor(pixelPos, startR, startG, startB)) {
          y += 1;

          fillData.data[pixelPos]     = fillColor.r;
          fillData.data[pixelPos + 1] = fillColor.g;
          fillData.data[pixelPos + 2] = fillColor.b;
          fillData.data[pixelPos + 3] = 255;


          if (x > 0) {
            if (matchStartColor(pixelPos - 4, startR, startG, startB)) {
              if (!neighborLeft) {
                // Add pixel to stack
                pixelStack.push([x - 1, y]);
                neighborLeft = true;
              }
            } else if (neighborLeft) {
              neighborLeft = false;
            }
          }

          if (x < (canvasWidth-1)) {
            if (matchStartColor(pixelPos + 4, startR, startG, startB)) {
              if (!neighborRight) {
                // Add pixel to stack
                pixelStack.push([x + 1, y]);
                neighborRight = true;
              }
            } else if (neighborRight) {
              neighborRight = false;
            }
          }

          pixelPos += canvasWidth * 4;
        }
      }
    }

    // Start a floodfill
    // 1. Get the color under the mouseclick
    // 2. Replace all of that color with the new color
    // 3. But respect bounding areas! Replace only contiguous color.
    function paintAt(startX, startY) {

      // get the clicked pixel's [r,g,b,a] color data
      var pixelPos = (startY * canvasWidth + startX) * 4,
        r = fillData.data[pixelPos],
        g = fillData.data[pixelPos + 1],
        b = fillData.data[pixelPos + 2],
        a = fillData.data[pixelPos + 3];

      // this pixel's already filled
      if (r === fillColor.r && g === fillColor.g && b === fillColor.b) {
        return;
      }

      // this pixel is part of the original black image--don't fill
      if (matchstrokeColor(r, g, b, a)) {
        return;
      }

      // execute the floodfill
      floodFill(startX, startY, r, g, b);

      // put the colorized data back on the canvas
      context.clearRect(0, 0, canvasWidth, canvasHeight);
      context.putImageData(fillData, 0, 0);
      context.drawImage(img,0,0);
    }

    // create a random color object {red,green,blue}
    function randomColorRGB(){
        var hex=Math.floor(Math.random()*16777215).toString(16);
        var r=parseInt(hex.substring(0,2),16);
        var g=parseInt(hex.substring(2,4),16);
        var b=parseInt(hex.substring(4,6),16);
        return({r:r,g:g,b:b});    
    }

    // draw the image to the canvas and get its pixel array
    // listen for mouse clicks and do floodfill when clicked
    function start() {

      context.drawImage(img,0,0);
      strokeData = context.getImageData(0, 0, canvasWidth, canvasHeight);
      context.clearRect(0, 0, context.canvas.width, context.canvas.height);
      fillData = context.getImageData(0, 0, canvasWidth, canvasHeight);
      context.drawImage(img,0,0);

      $('#canvas').mousedown(function (e) {
        // Mouse down location
        var mouseX=parseInt(e.clientX-offsetX);
        var mouseY=parseInt(e.clientY-offsetY);
        // set a new random fillColor
        fillColor=randomColorRGB();
        // floodfill
        paintAt(mouseX, mouseY);
      });

    };



}); // end $(function(){});

</script>

  </head>
  <body>
        <p>Click inside a shape below</p>
        <canvas id="canvas" width=210 height=300></canvas><br/>
  </body>
</html>