Reputation: 45
I'm trying to recreate this effect, but I want this to be in canvas, basically drawing squares with fadein effect using a loop. The loop part is ok, what I cannot figure out is the fade in effect. I'm using requestAnimationFrame, with each repaint the globalAlpha is incremented, the old sqaure is deleted and a new one drawn. This is the function:
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);
});
}
But I just can't get it to work. Here is a codepen with the whole thing.
http://codepen.io/easynowbaby/pen/GJKOej?editors=001
Ultimately, I want to be able to use the function in the loopSquares function. Any help is very appreciated. Cheers!
EDIT: I should have made myself clearer. I don't want to recreate the gallery with images, I'm only interested in the cascading fade in effect of squares. I want to achieve this effect in canvas where I'm gonna fade in little squares using fillRect function.
Upvotes: 1
Views: 4089
Reputation:
The first thing to point out here is how you use the requestID
to set the alpha. From MDN (my emphasis):
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.
In other words, don't assume this will keep a running value equivalent to current index of the cell. It may do so in one browser, accidentally, but no in another. Keep track of this value by other means.
Second, the globalAlpha
works on the entire context for anything drawn next to it. This means you need to track current alpha per square, or use a color style rgba which allow you to set alpha per style. This doesn't matter so much though as you need to track alpha also here.
I would suggest using an object for this, a square-monkey which can be trained to set its alpha correctly, then duplicated across a grid.
Main object will keep track of all settings such as current alpha, how much to update, what color and so forth. It's of course not limited to these alone - you could add scale, rotation etc. to it as well, but here I'll only show for 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
}
To save some memory and increase performance we can use prototypes for the common functions/methods:
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);
}
};
What we need to do now is to define a grid and cell size:
var cols = 10,
rows = 6,
cellWidth = canvas.width / cols,
cellHeight = canvas.height /rows;
Now we can populate the grid with an object per cell:
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));
}
}
And finally we need to trigger these square in any fashion we like, here just a diagonal cascading way. Lets also keep track of if all are done and not:
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>
By using a single object you can maintain code in a single place. For the fun of it, lets add rotation and scale as well:
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>
Upvotes: 6
Reputation: 11597
This is not a canvas nor a canvas effect.
Those are simple image tags animated by size and opacity. Just drag any of the images to the URL bar and you'll see the full-resolution image opens. This is so because they are DOM elements. If this was a canvas you couldn't possibly drag them to the URL bar.
All you need to do is animate one image tag properly (size and opacity). Both are really simple and well-explained for jQuery. Next, combine that effect in an array with a delay betweem them and you are on.
Upvotes: 1