admin管理员组文章数量:1420095
I have a shape, let's say:
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);
The shape will likely be different every time.
I have 10 gradients of blue and would like to randomly paint pixels contained within the shape as time goes on.
I have no idea how to even get the list of pixels contained within the shape, so that I could edit these.
Also I reckon that it may be a performance hit to redraw this each frame.
Any ideas how I would go on about this? Thanks! :)
I have a shape, let's say:
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);
The shape will likely be different every time.
I have 10 gradients of blue and would like to randomly paint pixels contained within the shape as time goes on.
I have no idea how to even get the list of pixels contained within the shape, so that I could edit these.
Also I reckon that it may be a performance hit to redraw this each frame.
Any ideas how I would go on about this? Thanks! :)
Share Improve this question edited Jun 29, 2013 at 15:20 Siot 951 silver badge6 bronze badges asked Jun 29, 2013 at 15:16 RadiantHexRadiantHex 25.6k47 gold badges155 silver badges251 bronze badges3 Answers
Reset to default 5Your solution is actually a bit plex because of the problem below!
Problem:
- You need a KineticJS shape object to draw your custom shape.
- The Shape will only allow 1 single context.beginPath in its drawFunc.
- Html Canvas only allows you to set 1 fillStyle (fill color) for each beginPath.
- That means you’re limited to filling with 1 single blue color—not your 10 shades of blue.
Solution:
- You can make your shape a clipping region.
- Then use another background canvas showing through your clipped shape.
- The background canvas will be used to efficiently draw individual pixels in all your shades of blue!
To draw random pixels and still cover all pixels, create a "map" array:
- Create an array with the index# of every pixel on the stage. ( randomPixels[] )
- Randomize the array.
- Use the randomized array containing every pixel index to draw random pixels 1-at-a-time.
This is the function to create the map-array:
// 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;
}
Next create the custom KineticJS Shape:
- Define your custom shape as a clipping region (in drawFunc)
- DrawImage the background canvas (which will contain constantly updating pixels)
- Since it’s clipped, the background canvas will only be visible inside your custom shape.
This creates a custom Kinetic Shape using your shape as a clipping path to a canvas background
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
});
To animate your 10 colors into your shape pixel by pixel
- Incrementally fill the background canvas with your colored pixels.
- Since the background canvas is “built-into” the kinetic Shape, all you have to do is draw a pixel on the canvas and then call layer.draw to make the change visible in your shape.
This function adds batches of randomly placed pixels to the background using 10 blue colors
// 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();
}
}
A Note: to keep performance high---and also to keep viewer’s patient(!), I draw a batch of pixels at once.
The proper mix of blue shades is maintained.
Of course, you would style to your tastes.
Here is code and a Fiddle: http://jsfiddle/m1erickson/zhatS/
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Prototype</title>
<script type="text/javascript" src="http://code.jquery./jquery.min.js"></script>
<script src="http://d3lp1msu2r81bx.cloudfront/kjs/js/lib/kinetic-v4.5.1.min.js"></script>
<style>
#container{
border:solid 1px #ccc;
margin-top: 10px;
width:300px;
height:300px;
}
canvas{border: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>
well if you want to know the x,y of the pixels in the canvas you can use a loop like the following:
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
}
}
if x/y positioning isn't important you can reduce it to a single loop (same setup as before).
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
}
then you want to re-paint the image to the canvas.
context.putImageData(image, 0, 0);
putImageData is a little expensive however if you are manipulating gradients this seems to be the quickest route I have found.
I don't know how familiar are you with graph search, but either Breadth first search or Depth first search might help you here. All you need to know is one starting pixel that is 100% in the shape.
Edit: I have done some search and this algorithm is basically the one I have proposed.
本文标签: javascriptHTML5 CanvasFilling a shape with random pixel colorsStack Overflow
版权声明:本文标题:javascript - HTML5 Canvas - Filling a shape with random pixel colors? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745327451a2653658.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论