画布:逐像素绘制图像和requestAnimationFrame时序

时间:2016-05-16 00:50:44

标签: javascript html5 canvas requestanimationframe

出于学习目的,我正在编写一些代码来在画布中逐个像素地随机绘制图像。这是以下代码的link

我希望动画在1秒内完成。但是,从控制台可以看到,大约需要7秒。我尝试较小的图像,数字接近1秒。

所以在这种情况下,requestAnimationFrame的时间不可靠。我想知道原因。是因为putImageData寻找其数据数组中的一个像素需要花费太多时间吗?或者是因为别的东西。我想知道requestAnimationFrame的时序何时不可靠对于制作好的动画至关重要。

此外,有没有更好的方法来做我想做的事情?

// At first, I get the image's data, then I build an array whose 
// length is equal to the number of pixels of the image, each element 
// of the array represents one of the image's pixel. Then I shuffle
// this array and pop a certain number of elements for 
// `ctx.putImageData` to draw the corresponding pixel per frame.

var ctx = c.getContext("2d"),
    img = new Image();
img.onload = init;
img.src = "download.png"; //placehold.it's 300x200 image

function formArr(w,h){ //Build image pixel outputting sequence array based on image's width and height
  var arr = [];
  for (i=0;i<w*h;i++){
    arr.push(i);
  }
  return arr;
}

function Point(i,w){ //locate pixel's X and Y base on image width
  this.x = i%w;
  this.y = Math.floor(i/w);
}

function shuffleRect(arr){  //shuffle the output sequence array
   ....
}

function init(){
  var w = ctx.canvas.width = img.width*2;
  var h = ctx.canvas.height = img.height*2;
  //form Image Data
  ctx.drawImage(img,0,0,w,h);
  var imageData = ctx.getImageData(0,0,w,h);
  ctx.clearRect(0,0,w,h);

  //build output sequence
  var sequence = formArr(w,h);
  shuffleRect(sequence);

  var sec = 1; //animation duration 
  var t1 = Date.now();
  function animate(){
    var pointsPerFrame = Math.floor(w*h/sec/60)+1;
    for (i=0;i<Math.min(pointsPerFrame,sequence.length);i++){
      var j = sequence.pop();
      ctx.putImageData(imageData,0,0,new Point(j,w).x,new Point(j,w).y,1,1); //draw points for next frame

    }
    if(sequence.length){requestAnimationFrame(animate)}else{
      var t2 = Date.now();
      console.log(t2-t1);

    }
  }
  animate();
}

1 个答案:

答案 0 :(得分:3)

使用drawImage的剪辑版本会比putImageData

更快
// directly copy from image to canvas 1 pixel at a time
context.drawImage(image,x,y,1,1,x,y,1,1);

但是使用合成可能比putImageData&amp;更快的drawImage ...

首先在内存中创建第二个画布大小的图片。

然后在每个动画循环中:

  • 在第二个画布上填写所需数量的新随机像素:memoryContext.beginPath + memoryContext.rect(x,y,1,1)每个新像素+ memoryContext.fill()
  • 清除主画布。
  • 将辅助画布绘制到主画布上:drawImage(memoryCanvas,0,0)
  • 在主画布上将合成设置为source-atop
  • drawImage将图片放到主画布上。合成将导致图像仅出现填充像素的位置。
  • 在主画布上将合成设置回source-over

&#13;
&#13;
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var canvas2=document.createElement("canvas");
var ctx2=canvas2.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

var newPixelCount;
var accumPixelCount=0;
var totPixels;
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/annotateMe.jpg";
function start(){
    cw=canvas.width=canvas2.width=img.width;
    ch=canvas.height=canvas2.height=img.height;
    newPixelCount=cw*ch/60;
    totPixels=cw*ch;
    t1=performance.now();
    requestAnimationFrame(animate);
}

function animate(){
    ctx2.beginPath();
    for(var i=0;i<newPixelCount;i++){
        accumPixelCount++;
        if(accumPixelCount<totPixels){
            var y=parseInt(accumPixelCount/cw);
            var x=accumPixelCount-y*cw;
            ctx2.rect(x,y,1,1);
        }
    }
    ctx2.fill();
    ctx.clearRect(0,0,cw,ch);
    ctx.drawImage(canvas2,0,0);
    ctx.globalCompositeOperation='source-atop';
    ctx.drawImage(img,0,0);
    ctx.globalCompositeOperation='source-over';
    //
    if(accumPixelCount<totPixels){
        requestAnimationFrame(animate);
    }else{
        alert('Complete: '+parseInt(performance.now()-t1)+'ms');
    }
}

function animateDrawImage(){
    ctx2.beginPath();
    for(var i=0;i<newPixelCount;i++){
        accumPixelCount++;
        if(accumPixelCount<totPixels){
            var y=parseInt(accumPixelCount/cw);
            var x=accumPixelCount-y*cw;
            ctx.drawImage(img,x,y,1,1,x,y,1,1);
        }
    }
    //
    if(accumPixelCount<totPixels){
        requestAnimationFrame(animate);
    }else{
        alert('Complete: '+parseInt(performance.now()-t1)+'ms');

    }
}
&#13;
<canvas id="canvas" width=300 height=300></canvas>
&#13;
&#13;
&#13;