Fading effect
So the fading effect would be using a loop of timed-out call to simulate. It will be something like this...
var painter = $('<canvas>').attr('width', $(window).width()).attr('height', $(window).height())[0],
pctx = painter.getContext('2d'), ctx = $('#canvas2')[0].getContext('2d'), img= $('img')[0];
for(var i = i; i <= 10; i++){
(function(i){
setTimeout(function(){
pctx.strokeStyle = "rgba(0,0,0,"+(1 - i / 10)+")";
pctx.beginPath();
pctx.moveTo(760, 20);
pctx.lineTo(760, 100);
pctx.stroke();
ctx.drawImage(img, 0, 0, img.width, img.height);
ctx.globalCompositeOperation = "destination-in";
ctx.drawImage(painter, 0, 0);
ctx.globalCompositeOperation = "source-over";
},i * 200);
})(i);
}
But I cannot make it directly work with mousemove event, because it fires for every pixel mouse move which is very costly and inefficient. And also for every line it draws, it draws also one image and one canvas as well, which is also very expensive.
This is what I'm going to do. Firstly, the event, instead of directly drawing, it just records the current mouse coordinate and the time stamp and trigger one recursive function if it's not started. And then the recursive function, which will constantly run once in a short while till no more event occurs, it contains the logic of drawing, coordinate collection adding and removal and the indicator that decide whether it can be triggered. Finally, I need a collection that stores coordinates so that I can draw all of them in the painter and copy the painter to the main canvas.
Collect the coordinates
The first thing is, I'm going to need an array to store coordinate objects.
var points = [];
Handle the mousemove event
Firstly I instantiate several global variables so I can read in the recursive function. And in the event handler, I record the coordinate and time stamp and trigger the recursive function if it’s not started yet.
var x, y, eventTimestamp, isContinue;
$(document).on('mousemove', function(e){
x = e.clientX, y = e.clientY, eventTimestamp = Date.now;
if(!isContinue) draw();
});
The recursive function
This function does basically three things.
- It adds and removes coordinates of the collection.
- It checks and decides whether it continues and can be triggered by the event handler.
- It draws all lines using adjacent coordinates and put it to the main canvas.
This function makes a delayed call to itself if mousemove event keeps firing. And when it executes it adds the new coordinate object to the collection. I can use this for the fading effect. So I put time stamps in coordinate objects and compare them with the newest one to decide the opacity value. By doing this, it draws lines with different fading effects and refreshes the canvas every time it executes. That's how the amination works.
function draw(){
var currentTimestamp = Date.now, newPoint = {x: x, y: y, t: currentTimestamp};
//check if to continue
isContinue = currentTimestamp - eventTimestamp < 500;
//add new coordinate
if(isContinue){
points.unshift(newPoint);
}
//remove faded out coordinates
for(var i = 0; i < points.length;){
currentTimestamp - points[i].t > 1000 ? points.splice(i, points.length) : i++;
}
//recursive call
if(points.length){
setTimeout(draw, 20);
}
var painter = $('<canvas>').attr('width', $(window).width()).attr('height', $(window).height())[0], pctx = painter.getContext('2d');
//clear painter, ready to draw
pctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
//draw all lines in the collection
for (var i = 1; i < points.length; i++) {
pctx.strokeStyle = "rgba(0,0,0," + Math.max(1-(currentTimestamp-points[i].t)/1000, 0) + ")";
//umcomment this line to get dynamic line width
//pctx.lineWidth = 25+75*Math.max(1-Math.sqrt(Math.pow(points[i].x-points[i-1].x,2)+Math.pow(points[i].y-points[i-1].y,2))/50,0);
pctx.lineWidth = 50;
pctx.beginPath();
pctx.moveTo(points[i - 1].x, points[i - 1].y);
pctx.lineTo(points[i].x, points[i].y);
pctx.stroke();
}
var ctx = $('canvas')[0].getContext('2d'), img= $('img')[0];
//copy-paste
ctx.drawImage(img, 0, 0, img.width, img.height);
ctx.globalCompositeOperation = "destination-in";
ctx.drawImage(painter, 0, 0);
ctx.globalCompositeOperation = "source-over";
}
Handle resize
The background will automatically re-adjust to adopt the new width or height. So the image and canvases are also need to be taken care of.
$(window).on('resize', function(){
var img = $('img')[0];
var windowHeight = $(window).height(), windowWidth = $(window).width(), windowRate = windowWidth/windowHeight;
var imgHeight = img.naturalHeight, imgWidth = img.naturalWidth, imgRate = imgWidth/imgHeight;
if(windowRate>=imgRate){
$(img).attr('height', (imgHeight/imgWidth*windowWidth)).attr('width', windowWidth);
}else{
$(img).attr('width', (imgWidth/imgHeight*windowHeight)).attr('height', windowHeight);
}
$('canvas').attr('width', windowWidth).attr('height', windowHeight);
});
So that's the all of it. I put the demo effect on the background. Feel free to try.
UNBLUR BACKGROUND (PART 1)
UNBLUR BACKGROUND (PART 2)