问题
这里首先要指出的是如何使用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
}
现场示例
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>