2013-06-29 100 views
3

我有一个形状,让我们说:HTML5帆布 - 用随机像素颜色填充形状?

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个梯度和想随机漆包含的形状内的像素随着时间的推移。


我不知道如何获得形状中包含的像素列表,以便我可以编辑这些。

另外我认为,这可能是命中重绘此每个帧的性能。


任何想法,我会怎么继续呢?谢谢! :)

回答

5

您的解决方案实际上是一个有点复杂,因为下面的问题!

问题:

  • 你需要一个KineticJS形状对象绘制自定义形状。
  • Shape在其drawFunc中只允许1个单独的context.beginPath。
  • Html Canvas只允许您为每个beginPath设置1个fillStyle(填充颜色)。
  • 这意味着你仅限于填充1个蓝色 - 而不是你的10个蓝色。

解决方案:

  • 你可以让你形状的裁剪区域。
  • 然后使用另一个背景画布显示通过裁剪后的形状。
  • 背景画布将用于高效地绘制您的所有蓝色阴影中的单个像素!

enter image description here

要绘制随机像素,并且仍然覆盖所有的像素中,创建一个“映射”数组:

  • 用在舞台上的每个像素的索引#创建的阵列。 (randomPixels [])
  • 随机化数组。
  • 使用包含每个像素索引的随机数组一次一个地绘制随机像素。

这是创建地图阵列功能:

// 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; 
} 
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> 
+0

哇!这真是太神奇了!这正是我想要做的。我也学到了很多东西,你也可以阅读你的答案。谢谢。 – RadiantHex

0

我不知道如何熟悉你用图形搜索,但无论是广度优先搜索或深度优先搜索可能会帮助你在这里。所有你需要知道的是一个起始像素是100%的形状。

编辑: 我已经做了一些搜索,this algorithm基本上是我提出的。

2

好,如果你想知道X,在画布上的像素的γ您可以像使用一个循环以下:

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是有点贵,但如果你操纵渐变,这似乎是我找到的最快的路线。