在HTML5 Canvas中动画不透明度

时间:2015-04-19 10:31:33

标签: javascript canvas

我正在尝试重新创建this effect,但我希望它在画布中,基本上使用循环绘制具有淡化效果的正方形。循环部分是好的,我无法弄清楚的是淡入效果。我正在使用requestAnimationFrame,每次重绘globalAlpha都会增加,删除旧的sqaure并绘制一个新的sqaure。这是功能:

function renderSquare(x, y) {

requestID = requestAnimationFrame(renderSquare);
alpha = requestID/100;  

ctx.clearRect(x,y,size,size);
ctx.globalAlpha = alpha;

var colour = "rgb(58,150,270)";
ctx.fillStyle = colour;
ctx.fillRect(x, y, size, size);


console.log("ID: " + requestID);  
console.log("Alpha: " + alpha);

if(alpha == 1) {
    cancelAnimationFrame(requestID);
}   

};

function drawSquare(x,y) {
  requestAnimationFrame(function render(){
    renderSquare(x,y);          
  });   
}

但我无法让它发挥作用。这是一个完整的代码。

http://codepen.io/easynowbaby/pen/GJKOej?editors=001

最终,我希望能够在loopSquares函数中使用该函数。非常感谢任何帮助。干杯!

编辑:我应该让自己更清楚。我不想用图像重新创建图库,我只对平方效果的级联淡化感兴趣。我想在画布中实现这个效果,我将使用fillRect函数淡化小方块。

2 个答案:

答案 0 :(得分:3)

问题

首先要指出的是如何使用requestID来设置alpha。来自MDN(我的重点):

  

一个长整数值,即请求ID,唯一标识   回调列表中的条目。这是一个非零值,但你可能没有   对其价值做出任何其他假设。您可以将此值传递给   window.cancelAnimationFrame()取消刷新回调请求。

换句话说,不要假设这将保持一个等于当前单元格索引的运行值。它可能在一个浏览器中意外地执行,但在另一个浏览器中没有。通过其他方式跟踪此值。

其次,globalAlpha适用于其旁边绘制的任何内容的整个上下文。这意味着您需要跟踪当前alpha per 方块,或使用颜色样式rgba,它允许您为每个样式设置alpha。这并不重要,因为你还需要跟踪alpha。

一种解决方案

我建议使用一个对象,一个方形猴子,可以训练正确设置其alpha,然后在网格上重复。

实施例

主要对象将跟踪所有设置,例如当前alpha,更新量,颜色等等。它当然不仅限于这些 - 你也可以添加比例,旋转等,但在这里我只会显示alpha:

// Square-monkey object
function Rectangle(ctx, x, y, w, h, color, speed) {

  this.ctx = ctx;
  this.x = x;
  this.y = y;
  this.height = h;
  this.width = w;
  this.color = color;

  this.alpha = 0;                        // current alpha for this instance
  this.speed = speed;                    // increment for alpha per frame
  this.triggered = false;                // is running
  this.done = false;                     // has finished
}

为了节省一些内存并提高性能,我们可以将原型用于常用功能/方法:

Rectangle.prototype = {

  trigger: function() {                  // start this rectangle
    this.triggered = true
  },

  update: function() {
    if (this.triggered && !this.done) {  // only if active
      this.alpha += this.speed;          // update alpha
      this.done = (this.alpha >= 1);     // update status
    }

    this.ctx.fillStyle = this.color;           // render this instance
    this.ctx.alpha = Math.min(1, this.alpha);  // clamp value
    this.ctx.fillRect(this.x, this.y, this.width, this.height);
  }  
};

我们现在需要做的是定义网格和单元格大小:

var cols = 10,
    rows = 6,
    cellWidth = canvas.width / cols,
    cellHeight = canvas.height /rows;

现在我们可以使用每个单元格的对象填充网格:

var grid = [],
    len = cols * rows,
    y = 0, x;

for(; y < rows; y++) {
  for(x = 0; x < cols; x++) {
    grid.push(new Rectangle(ctx, x * cellWidth, y * cellHeight,
                            cellWidth, cellHeight, "#79f", 0.25));
  }
}

最后我们需要以任何我们喜欢的方式触发这些方块,这里只是一个对角级联方式。让我们跟踪是否所有事情都已完成:

function loop() {
  ctx.globalAlpha = 1;           // reset alpha
  ctx.clearRect(0, 0, w, h);     // clear canvas

  // trigger cells
  for(y = 0; y < rows; y++) {    // iterate each row
    var gx = (x|0) - y;          // calc an x for current row
    if (gx >= 0 && gx < cols) {  // within limit?
      index = y * cols + gx;     // calc index
      grid[index].trigger();     // trigger this cell's animation if not running
    }
  }

  x += 0.25;                     // how fast to cascade

  hasActive = false;             // enable ending the animation when all done

  // update/render all cells
  for(var i = 0; i < grid.length; i++) {
    grid[i].update();
    if (!grid[i].done) hasActive = true;      // anyone active?
  }

  if (hasActive) requestAnimationFrame(loop); // animate if anyone's active
}

实例

&#13;
&#13;
var canvas = document.querySelector("canvas"),
    ctx = canvas.getContext("2d"),
    w = canvas.width,
    h = canvas.height;

// Square-monkey object
function Rectangle(ctx, x, y, w, h, color, speed) {

  this.ctx = ctx;
  this.x = x;
  this.y = y;
  this.height = h;
  this.width = w;
  this.color = color;
  
  this.alpha = 0;                        // current alpha for this instance
  this.speed = speed;                    // increment for alpha per frame
  this.triggered = false;                // is running
  this.done = false;                     // has finished
}

// prototype methods that will be shared
Rectangle.prototype = {

  trigger: function() {                  // start this rectangle
    this.triggered = true
  },
  
  update: function() {
    if (this.triggered && !this.done) {  // only if active
      this.alpha += this.speed;          // update alpha
      this.done = (this.alpha >= 1);     // update status
    }
      
    this.ctx.fillStyle = this.color;   // render this instance
    this.ctx.globalAlpha = Math.min(1, this.alpha);
    this.ctx.fillRect(this.x, this.y, this.width, this.height);
  }  
};

// Populate grid
var cols = 10,
    rows = 6,
    cellWidth = canvas.width / cols,
    cellHeight = canvas.height /rows,
    grid = [],
    len = cols * rows,
    y = 0, x;

for(; y < rows; y++) {
  for(x = 0; x < cols; x++) {
    grid.push(new Rectangle(ctx, x * cellWidth, y * cellHeight, cellWidth, cellHeight, "#79f", 0.025));
  }
}

var index,
    hasActive = true;

x = 0;

function loop() {

  ctx.globalAlpha = 1;
  ctx.clearRect(0, 0, w, h);
  
  // trigger cells
  for(y = 0; y < rows; y++) {
    var gx = (x|0) - y;
    if (gx >= 0 && gx < cols) {
      index = y * cols + gx;
      grid[index].trigger();
    }
  }
  
  x += 0.25;
  
  hasActive = false;
  
  // update all
  for(var i = 0; i < grid.length; i++) {
    grid[i].update();
    if (!grid[i].done) hasActive = true;
  }
  
  if (hasActive) requestAnimationFrame(loop)
}
loop();
&#13;
<canvas width=500 height=300></canvas>
&#13;
&#13;
&#13;

实例扩展对象

通过使用单个对象,您可以将代码保存在一个位置。为了好玩,我们也可以添加旋转和缩放:

&#13;
&#13;
var canvas = document.querySelector("canvas"),
    ctx = canvas.getContext("2d"),
    w = canvas.width,
    h = canvas.height;

// Square-monkey object
function Rectangle(ctx, x, y, w, h, color, speed) {

  this.ctx = ctx;
  this.x = x;
  this.y = y;
  this.height = h;
  this.width = w;
  this.color = color;
  
  this.alpha = 0;                        // current alpha for this instance
  this.speed = speed;                    // increment for alpha per frame
  this.triggered = false;                // is running
  this.done = false;                     // has finished
}

// prototype methods that will be shared
Rectangle.prototype = {

  trigger: function() {                  // start this rectangle
    this.triggered = true
  },
  
  update: function() {
    if (this.triggered && !this.done) {  // only if active
      this.alpha += this.speed;          // update alpha
      this.done = (this.alpha >= 1);     // update status
    }

    this.ctx.fillStyle = this.color;     // render this instance
    this.ctx.globalAlpha = Math.min(1, this.alpha);
    
    var t = this.ctx.globalAlpha,        // use current alpha as t
        cx = this.x + this.width * 0.5,  // center position
        cy = this.y + this.width * 0.5;
    
    this.ctx.setTransform(t, 0, 0, t, cx, cy); // scale and translate
    this.ctx.rotate(0.5 * Math.PI * (1 - t));  // rotate, 90° <- alpha
    this.ctx.translate(-cx, -cy);              // translate back
    this.ctx.fillRect(this.x, this.y, this.width, this.height);
  }  
};

// Populate grid
var cols = 20,
    rows = 12,
    cellWidth = canvas.width / cols,
    cellHeight = canvas.height /rows,
    grid = [],
    len = cols * rows,
    y = 0, x;

for(; y < rows; y++) {
  for(x = 0; x < cols; x++) {
    grid.push(new Rectangle(ctx, x * cellWidth, y * cellHeight, cellWidth, cellHeight, "#79f", 0.02));
  }
}

var index,
    hasActive = true;

x = 0;

function loop() {

  ctx.setTransform(1,0,0,1,0,0);
  ctx.globalAlpha = 1;
  ctx.clearRect(0, 0, w, h);
  
  // trigger cells
  for(y = 0; y < rows; y++) {
    var gx = (x|0) - y;
    if (gx >= 0 && gx < cols) {
      index = y * cols + gx;
      grid[index].trigger();
    }
  }
  
  x += 0.333;
  
  hasActive = false;
  
  // update all
  for(var i = 0; i < grid.length; i++) {
    grid[i].update();
    if (!grid[i].done) hasActive = true;
  }
  
  if (hasActive) requestAnimationFrame(loop)
}
loop();
&#13;
<canvas width=500 height=300></canvas>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

这不是画布也不是画布效果。

这些是由大小和不透明度动画的简单图像标签。 只需将任意图像拖动到URL栏,您就会看到全分辨率图像打开。这是因为它们是DOM元素。如果这是一个画布,你就不可能将它们拖到URL栏。

您需要做的就是正确设置一个图像标签的动画(大小和不透明度)。两者都非常简单,并且很好地解释了jQuery。接下来,将这个效果组合在一个数组中,然后延迟它们,你就可以了。