HTML5 Canvas - 用随机像素颜色填充形状?
Posted
技术标签:
【中文标题】HTML5 Canvas - 用随机像素颜色填充形状?【英文标题】:HTML5 Canvas - Filling a shape with random pixel colors? 【发布时间】:2013-06-27 05:28:50 【问题描述】:我有一个形状,比如说:
var context = canvas.getContext();
context.beginPath();
context.moveTo(200, 50);
context.lineTo(420, 80);
context.quadraticCurveTo(300, 100, 260, 170);
context.closePath();
canvas.fillStroke(this);
形状可能每次都不一样。
我有 10 个渐变的蓝色,并希望随着时间的推移随机绘制形状中包含的像素。
我什至不知道如何获取形状中包含的像素列表,以便我可以编辑这些。
我还认为重绘每一帧可能会影响性能。
任何想法我将如何继续这件事?谢谢! :)
【问题讨论】:
【参考方案1】:由于下面的问题,您的解决方案实际上有点复杂!
问题:
您需要一个 KineticJS 形状对象来绘制您的自定义形状。 Shape 在其 drawFunc 中只允许 1 个 context.beginPath。 html Canvas 仅允许您为每个 beginPath 设置 1 个填充样式(填充颜色)。 这意味着您只能填充 1 种蓝色,而不是 10 种蓝色。解决方案:
您可以将形状设置为剪切区域。 然后使用另一个背景画布显示您剪切的形状。 背景画布将用于有效地绘制所有蓝色阴影中的单个像素!要绘制随机像素并仍然覆盖所有像素,请创建一个“地图”数组:
使用舞台上每个像素的 index# 创建一个数组。 (随机像素[]) 随机化数组。 使用包含每个像素索引的随机数组来一次绘制一个随机像素。这是创建地图数组的函数:
// make an array with elements from 0 to shapeWidth*shapeHeight in random order
function initPixelArray(shapeWidth,shapeHeight)
var array=[];
var i;
var countdown;
var temp;
// Must be a better way to fill an array with
// a sequential group of numbers, just in random order
// Anyone got some magic for this ????????????
// fill array with sequential numbers representing each pixel;
for(var i=0;i<shapeWidth*shapeHeight;i++) array.push(i);
// randomize the array
countdown=array.length;
while (countdown > 0)
i = (Math.random() * countdown--) | 0;
temp = array[countdown];
array[countdown] = array[i];
array[i] = temp;
//
return array;
接下来创建自定义 KineticJS 形状:
将您的自定义形状定义为剪切区域(在 drawFunc 中) DrawImage 背景画布(将包含不断更新的像素) 由于被裁剪,背景画布将仅在您的自定义形状内可见。这将创建一个自定义动力学形状,使用您的形状作为画布背景的剪切路径
var shape = new Kinetic.Shape(
drawFunc: function(canvas)
var context = canvas.getContext();
context.beginPath();
context.moveTo(0, 50);
context.lineTo(220, 80);
context.quadraticCurveTo(100, 100, 60, 170);
context.closePath();
context.clip();
context.drawImage(this.backgroundCanvas,0,0);
canvas.fillStroke(this);
,
x: x,
y: y,
stroke: "lightgray",
strokeWidth: 8,
draggable:true
);
将你的 10 种颜色逐个像素地动画到你的形状中
用彩色像素逐步填充背景画布。 由于背景画布“内置”在动态形状中,您只需在画布上绘制一个像素,然后调用 layer.draw 以使更改在形状中可见。此函数使用 10 种蓝色将一批随机放置的像素添加到背景中
// add pixels to the background canvas
// after layer.draw the new pixels will be visible in the shape
function updatePattern()
// add 10 pixels of each type of blue (100px total)
for(var n=0;n<shapeWidth/8;n++)
// set a blue color
tempCtx.fillStyle=color[n];
tempCtx.beginPath();
// draw 10 random pixels in that blue
for(var b=0;b<10;b++)
// get next random pixel
var i=randomPixels[nextPixel]
// calculate x/y from i
var y=Math.floor(i/shapeWidth);
var x=(i-y*shapeWidth)-1;
// draw
tempCtx.rect(x,y,1,1);
// increment array index and do bounds check
if(++nextPixel>=randomPixels.length)return;
// done filling with this blue--do the fill()
tempCtx.fill();
注意:为了保持高性能——同时也为了保持观众的耐心(!),我一次绘制了一批像素。
保持适当的蓝色调混合。
当然,你可以根据自己的喜好来设计。
这是代码和小提琴:http://jsfiddle.net/m1erickson/zhatS/
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Prototype</title>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.5.1.min.js"></script>
<style>
#container
border:solid 1px #ccc;
margin-top: 10px;
width:300px;
height:300px;
canvasborder:1px solid red;
</style>
<script>
$(function()
var stage = new Kinetic.Stage(
container: 'container',
width: 300,
height: 300
);
var layer = new Kinetic.Layer();
stage.add(layer);
// the KineticJS shape
var myShape;
//
var shapeWidth=stage.getWidth();
var shapeHeight=stage.getHeight();
// temporary canvas to create/update the pattern for the Shape
var tempCanvas=document.createElement("canvas");
var tempCtx=tempCanvas.getContext("2d");
tempCanvas.width=shapeWidth;
tempCanvas.height=shapeHeight;
// an array of pixel references from 0 to shapeWidth*shapeHeight
// used to draw individual pixels randomly
var randomPixels=initPixelArray(shapeWidth,shapeHeight);
var nextPixel=0;
// shades of colors to fill with
var color;
var colors=
blues:["#0000ff","#2b60de","#4863a0","#2554c7","#000080","#afdcec","#c6deff","#6698ff","#c2dfff","#79baec"],
reds:["#ff0000","#f4c3c2","#fd5c5c","#fe2625","#ff3030","#ff6666","#fa8072","#efc3bf","#ff6347","#ef7942"]
// Create a KineticJS shape
function kShape(x,y)
var shape = new Kinetic.Shape(
drawFunc: function(canvas)
var context = canvas.getContext();
context.beginPath();
context.moveTo(0, 50);
context.lineTo(220, 80);
context.quadraticCurveTo(100, 100, 60, 170);
context.closePath();
context.clip();
context.drawImage(this.backgroundCanvas,0,0);
canvas.fillStroke(this);
,
x: x,
y: y,
stroke: "lightgray",
strokeWidth: 8,
draggable:true
);
// let the shape keep a reference to the background canvas
shape.backgroundCanvas=tempCanvas;
layer.add(shape);
layer.draw();
return(shape);
// make an array with elements from 0 to shapeWidth*shapeHeight in random order
function initPixelArray(shapeWidth,shapeHeight)
var array=[];
var i;
var countdown;
var temp;
// Must be a better way to fill an array with
// a sequential group of numbers, just in random order
// Anyone got some magic for this ????????????
// fill array with sequential numbers representing each pixel;
for(var i=0;i<shapeWidth*shapeHeight;i++) array.push(i);
// randomize the array
countdown=array.length;
while (countdown > 0)
i = (Math.random() * countdown--) | 0;
temp = array[countdown];
array[countdown] = array[i];
array[i] = temp;
//
return array;
// add pixels to the pattern
function updatePattern()
// add 10 pixels of each type of blue (100px total)
for(var n=0;n<shapeWidth/8;n++)
// set a blue color
tempCtx.fillStyle=color[n];
tempCtx.beginPath();
// draw 10 random pixels in that blue
for(var b=0;b<10;b++)
// get next random pixel
var i=randomPixels[nextPixel]
// calculate x/y from i
var y=Math.floor(i/shapeWidth);
var x=(i-y*shapeWidth)-1;
// draw
tempCtx.rect(x,y,1,1);
// increment array index and do bounds check
if(++nextPixel>=randomPixels.length)return;
// done filling with this blue--do the fill()
tempCtx.fill();
// great cross-browser animation shim by Paul Irish
window.requestAnimFrame = (function(callback)
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback)
window.setTimeout(callback, 1000 / 60);
;
)();
// animate filling of pixels
var fps = 60;
function animate()
setTimeout(function()
requestAnimationFrame(animate);
if(nextPixel>=randomPixels.length)return;
updatePattern();
layer.draw();
, 1000 / fps);
////////////////// ACTION STARTS HERE
var myShape=kShape(20,20);
$("#blues").click(function()
color=colors.blues;
nextPixel=0;
animate();
);
$("#reds").click(function()
color=colors.reds;
nextPixel=0;
animate();
);
); // end $(function());
</script>
</head>
<body>
<div id="container"></div>
<button id="blues">Fill blues</button>
<button id="reds">Fill reds</button>
</body>
</html>
【讨论】:
哇!这真是太棒了!这正是我想要做的。阅读您的答案,我也学到了很多东西。谢谢。【参考方案2】:如果你想知道画布中像素的 x,y,可以使用如下循环:
var image = context.getImageData(0, 0, width, height),
data = image.data;
for( var x = 0; x < width; x++ )
for( var y = 0; y < height; y++)
var i = (x*4)+(y*4*image.width);
data[i] = ..; // I am the red
data[i+1] = ..; // I am the green
data[i+2] = ..; // I am the blue
data[i+3] = ..; // I am the alpha
如果 x/y 定位不重要,您可以将其简化为一个循环(与之前的设置相同)。
for( var i = 0; i < data.length; i+=4)
data[i] = ..; // I am the red
data[i+1] = ..; // I am the green
data[i+2] = ..; // I am the blue
data[i+3] = ..; // I am the alpha
然后您想将图像重新绘制到画布上。
context.putImageData(image, 0, 0);
putImageData 有点贵,但是如果您要处理渐变,这似乎是我找到的最快的路线。
【讨论】:
【参考方案3】:我不知道您对图形搜索有多熟悉,但广度优先搜索或深度优先搜索可能对您有所帮助。您只需要知道一个 100% 形状的起始像素。
编辑: 我做了一些搜索,this algorithm 基本上是我提出的。
【讨论】:
以上是关于HTML5 Canvas - 用随机像素颜色填充形状?的主要内容,如果未能解决你的问题,请参考以下文章
HTML5画布Canvas文本填充线段属性裁剪透明度与像素合并方式