使用画布在setInterval上获取像素数据

时间:2014-11-07 09:19:48

标签: html5 animation canvas

我想要制作一个由粒子组成的动画字母。基本上,粒子从一个字母形状转换为另一个字母形状。

我的想法是快速将字母填充为画布上的文字(如框架),获取像素数据并将粒子放到setInterval上的正确位置。我现在有用于扫描屏幕的代码:

var ctx = canvas.getContext('2d'),
    width = ctx.canvas.width,
    height = ctx.canvas.height,

    particles = [],
    gridX = 8,
    gridY = 8;

function Particle(x, y) {
    this.x = x;
    this.y = y;
}

// fill some text
ctx.font = 'bold 80px sans-serif';
ctx.fillStyle = '#ff0';
ctx.fillText("STACKOVERFLOW", 5, 120);

// now parse bitmap based on grid
var idata = ctx.getImageData(0, 0, width, height);

// use a 32-bit buffer as we are only checking if a pixel is set or not
var buffer32 = new Uint32Array(idata.data.buffer);

// using two loops here, single loop with index-to-x/y is also an option
for(var y = 0; y < height; y += gridY) {
  for(var x = 0; x < width; x += gridX) {

    //buffer32[] will have a value > 0 (true) if set, if not 0=false
    if (buffer32[y * width + x]) {
      particles.push(new Particle(x, y));
    }
  }
}

// render particles
ctx.clearRect(0, 0, width, height);

particles.forEach(function(p) {
  ctx.fillRect(p.x - 2, p.y - 2, 4, 4); // just squares here
})

但是这样我只显示一个单词,并且在整个时间内没有任何变化。此外,我想最初设置200个粒子并根据像素数据重新组织它们,而不是在每次扫描时创建它们。你将如何重写代码,所以在每1500ms我可以传递一个不同的字母并用粒子渲染它?

1 个答案:

答案 0 :(得分:1)

希望这段代码的不同部分应该足够清晰:有粒子,可以绘制和更新,fillParticle会从文本字符串中产生粒子,而spawnChars将获得定期渲染的文本的新部分。

它工作得很好,如果你愿意,可以使用参数,它们都是小提琴的开头 您可能希望通过避免全局变量和创建类来使此代码更清晰。

http://jsbin.com/jecarupiri/1/edit?js,output

// --------------------
// parameters

var text = 'STACKOVERFLOW';
var fontHeight = 80;
var gridX = 4,
    gridY = 4;
var partSize = 2; 
var charDelay = 400; // time between two chars, in ms
var spead = 80; // max distance from start point to final point
var partSpeed = 0.012;

// --------------------
var canvas = document.getElementById('cv'),
    ctx = canvas.getContext('2d'),
    width = ctx.canvas.width,
    height = ctx.canvas.height,
    particles = [];

ctx.translate(0.5,0.5);

// --------------------
// Particle class
function Particle(startX, startY, finalX, finalY) {
    this.speed = partSpeed*(1+Math.random()*0.5);
    this.x = startX;
    this.y = startY;

    this.startX = startX;
    this.startY = startY;
    this.finalX =finalX;
    this.finalY =finalY;
    this.parameter = 0;
    this.draw = function() {
        ctx.fillRect(this.x - partSize*0.5, this.y - partSize*0.5, partSize, partSize);
    };
    this.update = function(p) {
       if (this.parameter>=1) return;
       this.parameter += partSpeed;
       if (this.parameter>=1) this.parameter=1;
       var par = this.parameter;
       this.x = par*this.finalX + (1-par)*this.startX;
       this.y = par*this.finalY + (1-par)*this.startY;
   };
}

// --------------------
// Text spawner

function fillParticle(text, offx, offy,  spread) {
   // fill some text
   tmpCtx.clearRect(0,0,tmpCtx.canvas.width, tmpCtx.canvas.height);
   tmpCtx.font = 'bold ' + fontHeight +'px sans-serif';
   tmpCtx.fillStyle = '#A40';
   tmpCtx.textBaseline ='top';
   tmpCtx.textAlign='left';
   tmpCtx.fillText(text, 0, 0);
   //
   var txtWidth = Math.floor(tmpCtx.measureText(text).width);
   // now parse bitmap based on grid
   var idata = tmpCtx.getImageData(0, 0, txtWidth, fontHeight);

   // use a 32-bit buffer as we are only checking if a pixel is set or not
   var buffer32 = new Uint32Array(idata.data.buffer);

   // using two loops here, single loop with index-to-x/y is also an option
   for(var y = 0; y < fontHeight; y += gridY) {
     for(var x = 0; x < txtWidth; x += gridX) {
        //buffer32[] will have a value > 0 (true) if set, if not 0=false
        if (buffer32[y * txtWidth + x]) {
           particles.push(new Particle(offx + x+Math.random()*spread - 0.5*spread, 
                                  offy + y+Math.random()*spread - 0.5*spread, offx+x, offy+y));
        }
    }
  }  
  return txtWidth;
}

var tmpCv = document.createElement('canvas');
// uncomment for debug
//document.body.appendChild(tmpCv);
var tmpCtx = tmpCv.getContext('2d');

// --------------------------------
// spawn the chars of the text one by one

var charIndex = 0;
var lastSpawnDate = -1;
var offX = 30;
var offY = 30;

function spawnChars() {
  if (charIndex>= text.length) return;
  if (Date.now()-lastSpawnDate < charDelay) return;
  offX += fillParticle(text[charIndex], offX, offY, spead);
  lastSpawnDate = Date.now();
  charIndex++;                           
}


// --------------------------------

function render() {
   // render particles
   particles.forEach(function(p) { p.draw();
   });  
}

function update() {
   particles.forEach(function(p) { p.update(); } );    
}

// --------------------------------
//  animation
function animate(){
   requestAnimationFrame(animate);
   ctx.clearRect(0, 0, width, height);
   render();
    update();
  //
  spawnChars();
}

// launch : 
animate();