您可以使用泛色填充为由用户定义的线限定的单击区域着色。
让用户在画布上画线。
当用户点击以线条为界的区域时,用颜色填充该区域。
注意:您必须在画布下方绘制网格线,否则这些网格线将充当填充算法的边界,您将只填充网格单元格。您可以使用 CSS 在画布下分层图像或使用单独的画布绘制网格线。

这是开始的示例代码和演示:http: //jsfiddle.net/m1erickson/aY4Xs/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
#canvas{border:1px solid red;}
</style>
<script>
$(function(){
// canvas and mousedown related variables
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var scrollX=$canvas.scrollLeft();
var scrollY=$canvas.scrollTop();
// save canvas size to vars b/ they're used often
var canvasWidth=canvas.width;
var canvasHeight=canvas.height;
// define the grid area
// lines can extend beyond grid but
// floodfill wont happen outside beyond the grid
var gridRect={x:50,y:50,width:200,height:200}
drawGridAndLines();
// draw some test gridlines
function drawGridAndLines(){
ctx.clearRect(0,0,canvas.width,canvas.height)
// Important: the lineWidth must be at least 5
// or the floodfill algorithm will "jump" over lines
ctx.lineWidth=5;
ctx.strokeRect(gridRect.x,gridRect.y,gridRect.width,gridRect.height);
ctx.beginPath();
ctx.moveTo(75,25);
ctx.lineTo(175,275);
ctx.moveTo(25,100);
ctx.lineTo(275,175);
ctx.stroke();
}
// save the original (unfilled) canvas
// so we can reference where the black bounding lines are
var strokeData = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
// fillData contains the floodfilled canvas data
var fillData = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
// Thank you William Malone for this great floodFill algorithm!
// http://www.williammalone.com/articles/html5-canvas-javascript-paint-bucket-tool/
//////////////////////////////////////////////
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;
}
}
}
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;
}
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);
}
// 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
ctx.putImageData(fillData, 0, 0);
}
// end floodFill algorithm
//////////////////////////////////////////////
// get the pixel colors under x,y
function getColors(x,y){
var data=ctx.getImageData(x,y,1,1).data;
return({r:data[0], g:data[1], b:data[2], a:data[3] });
}
// 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});
}
function handleMouseDown(e){
e.preventDefault();
// get the mouse position
x=parseInt(e.clientX-offsetX);
y=parseInt(e.clientY-offsetY);
// don't floodfill outside the gridRect
if(
x<gridRect.x+5 ||
x>gridRect.x+gridRect.width ||
y<gridRect.y+5 ||
y>gridRect.y+gridRect.height
){return;}
// get the pixel color under the mouse
var px=getColors(x,y);
// get a random color to fill the region with
fillColor=randomColorRGB();
// floodfill the region bounded by black lines
paintAt(x,y,px.r,px.g,px.b);
}
$("#canvas").mousedown(function(e){handleMouseDown(e);});
}); // end $(function(){});
</script>
</head>
<body>
<h4>Click in a region within the grid square.</h4>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
[关于 getImageData 和像素数组的信息]
context.getImageData().data
获取一个表示画布指定区域的 r、g、b 和一个值的数组(在我们的例子中,我们选择了整个画布)。左上角的像素 (0,0) 是数组中的第一个元素。
每个像素由阵列中的 4 个连续元素表示。
第一个数组元素保存红色分量 (0-255),下一个元素保存蓝色,下一个保存绿色,下一个保存 alpha(不透明度)。
// pixel 0,0
red00=data[0];
green00=data[1];
blue00=data[2];
alpha00=data[3];
// pixel 1,0
red10=data[4];
green10=data[5];
blue10=data[6];
alpha10=data[7];
因此,您可以像这样跳转到鼠标下方任意像素的红色元素:
// pixelPos is the position in the array of the first of 4 elements for pixel (mouseX,mouseY)
var pixelPos = (mouseY * canvasWidth + mouseX) * 4
您可以通过获取接下来的 4 个像素数组元素来获取所有 4 个 r,g,b,a 值
var r = fillData.data[pixelPos];
var g = fillData.data[pixelPos + 1];
var b = fillData.data[pixelPos + 2];
var a = fillData.data[pixelPos + 3];