2013-06-29 152 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是有點貴,但如果你操縱漸變,這似乎是我找到的最快的路線。