javascript - 在 HTML5 Canvas 中设置不透明度动画

标签 javascript canvas

我正在尝试重新创建 this effect ,但我希望它在 Canvas 中,基本上使用循环绘制具有淡入效果的正方形。循环部分还可以,我无法弄清楚的是淡入淡出效果。我正在使用 requestAnimationFrame,每次重绘时 globalAlpha 都会增加,旧的 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 函数中的函数。非常感谢任何帮助。干杯!

编辑:我应该让自己更清楚。我不想用图像重新创建画廊,我只对正方形的级联淡入淡出效果感兴趣。我想在 Canvas 中实现这种效果,我将使用 fillRect 函数在小方 block 中淡入淡出。

最佳答案

问题

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

A long integer value, the request id, that uniquely identifies the entry in the callback list. This is a non-zero value, but you may not make any other assumptions about its value. You can pass this value to window.cancelAnimationFrame() to cancel the refresh callback request.

换句话说,不要假设这会保持与当前单元格索引相等的运行值。它可能会在一个浏览器中不小心这样做,但在另一个浏览器中不会。通过其他方式跟踪此值。

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

一个解决方案

我建议为此使用一个对象,一个方形猴子,可以对其进行训练以正确设置其 alpha,然后在网格中复制。

例子

Main 对象将跟踪所有设置,例如当前的 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
}

为了节省一些内存并提高性能,我们可以为常用函数/方法使用原型(prototype):

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

最后我们需要以任何我们喜欢的方式触发这些方 block ,这里只是对 Angular 线级联方式。我们还可以跟踪是否完成所有操作:

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
}

实例

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();
<canvas width=500 height=300></canvas>

实例扩展对象

通过使用单个对象,您可以在一个地方维护代码。为了好玩,让我们也添加旋转和缩放:

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();
<canvas width=500 height=300></canvas>

关于javascript - 在 HTML5 Canvas 中设置不透明度动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29728631/

相关文章:

javascript - 如何用纯javascript控制奇观呈现?

javascript - 无法定位 Canvas 的子元素

javascript - 仅当设置新帧时才将动画绘制到 Canvas 上?

javascript - 将 Canvas 发送到 UDP 多播地址 - 多播 Canvas 直播流

Javascript - json 数据未正确传输到内存中

javascript - 如何从巴尔干图中的二进制图像数据加载图像

javascript - JQuery:获取RadioButtonList中的每个项目

javascript - 如何将字符串更改为 Google 图表数据

javascript - 如何只从 Canvas 中删除一条线,而不是所有的图画?

javascript - 在 JavaScript 中将颜色填充到 HTML Canvas 的特定部分,如 Windows Paint