简体   繁体   English

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

[英]Canvas: Draw image pixel by pixel and requestAnimationFrame timing

For learning purpose, I am writing some code to draw an image randomly pixel by pixel in a canvas. 出于学习目的,我正在编写一些代码来在画布中逐个像素地随机绘制图像。 Here's the link for the following codes. 这是以下代码的链接

I want the animation to be finished within 1 sec. 我希望动画在1秒内完成。 However, as you can see from the console, it takes about 7 sec. 但是,从控制台可以看到,大约需要7秒。 I try smaller image, the number is closing to 1 sec. 我尝试较小的图像,数字接近1秒。

So in this case, the timing of requestAnimationFrame is not reliable. 所以在这种情况下,requestAnimationFrame的时间不可靠。 I want to know the cause. 我想知道原因。 Is it because putImageData looking for one of the pixels in its data array takes too much time? 是因为putImageData在其数据数组中寻找其中一个像素需要花费太多时间吗? Or is it because something else. 或者是因为别的东西。 I think to know when requestAnimationFrame's timing is not reliable is critical to make good animations. 我想知道requestAnimationFrame的时序何时不可靠对于制作好的动画至关重要。

Moreover, is there any better way to do what I want to do? 而且,有没有更好的方法来做我想做的事情?

// 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();
}

Using the clipping version of drawImage will be faster than putImageData 使用drawImage的剪切版本将比putImageData更快

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

But using compositing might be faster than putImageData & drawImage... 但是使用合成可能比putImageData和drawImage更快......

First create a second canvas in memory the size of the image. 首先在内存中创建第二个画布大小的图像。

Then in each animation loop: 然后在每个动画循环中:

  • Fill the required number of new random pixels on the second canvas: memoryContext.beginPath + memoryContext.rect(x,y,1,1) each new pixel + memoryContext.fill() . 在第二个画布上填充所需数量的新随机像素: memoryContext.beginPath + memoryContext.rect(x,y,1,1)每个新像素+ memoryContext.fill()
  • Clear the main canvas. 清除主画布。
  • Draw the secondary canvas onto the main canvas: drawImage(memoryCanvas,0,0) 将辅助画布绘制到主画布上: drawImage(memoryCanvas,0,0)
  • Set compositing to source-atop on the main canvas. 在主画布上将compositing设置为source-atop
  • drawImage the image onto the main canvas. drawImage将图像放到主画布上。 Compositing will cause the image to appear only where the filled pixels exist. 合成将使图像仅出现填充像素的位置。
  • Set compositing back to source-over on the main canvas. 在主画布上将合成设置回source-over

 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'); } } 
 <canvas id="canvas" width=300 height=300></canvas> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM