Wednesday, October 12, 2011

Javascript Canvas trumps all others

Recently I have made a little test. I would have like to make a little active-background effect on my project and I have faced a problem with the hardware resources. What you have to know about this project is, that it will be an information panel on my college where I graduated. So, the hardware, the browser and the resolution (and pretty much everything else) is fixed.

This is the DEMO (and a simplified result):
http://jsfiddle.net/FilipFlora/cLByF/10/embedded/result/

The task was:
Create an active-background animation when the tablet is idle. So when you walk by, it'll catch your attention..

The problem:
The hardware is rather energy-efficient and has only a basic graphics-card integrated. The images to move are huge (circa 2000x1600) and they have a 5% opacity on them. (Just as I wrote these lines, I thought that another solution could be to set the opacity value with css and use gifs instead?? But I don't think that this would be better than the solution below)

The attempts to solve the problem: (not in this order, but I was diligent to find the best looking solution :) )
1. attempt: Background-image (for this I used the background-position jQuery plugin)
I tried that..yes. I created 4 layers, I have set the background-image with CSS, and created the animation with jQuery.

HTML:
<div id="index_bg_1">&nbsp;</div>
<div id="index_bg_2">&nbsp;</div>
<div id="index_bg_3">&nbsp;</div>
<div id="index_bg_4">&nbsp;</div>

CSS:
#index_bg_1,
#index_bg_2,
#index_bg_3,
#index_bg_4 {
 background-image: url("../images/index-bg-1.png");
 background-repeat: no-repeat;
 background-position: 0px 0px;
 width: 1680px;
 height: 1050px;
 position: absolute;
 left: 0px;
 top: 0px;
}

#index_bg_2 {
 background-image: url("../images/index-bg-2.png");
 background-position: 0px 0px;
}

#index_bg_3 {
 background-image: url("../images/index-bg-3.png");
 background-position: 0px 0px;
}

#index_bg_4 {
 background-image: url("../images/index-bg-4.png");
 background-position: 0px 0px;
}

Javascript:
jQuery(document).ready(function() {

 // initiate all background-image animation
 for( var i = 1; i<5; i++) {
  animateBackgroundImage("#index_bg_"+i);
 }
 
});

function animateBackgroundImage(selector) {
 
 // new target x coodinate
 var pos_x = Math.rand(-1200, 1600);
 // current x coordinate 
 var bg_x = ((jQuery(selector).css("background-position")).replace("px 0px", ""))*1;
 // animation time
 var anim_time = Math.abs(pos_x-bg_x)/24*1000;
 
 // to do this, you need the backgroundPosition plugin for jQuery
 jQuery(selector).animate({
  backgroundPosition: "("+pos_x+"px 0px)" // to keep it simple, let's animate only the x value
 }, anim_time, "linear" );
 
 // after the animation is done, create another
 setTimeout("animateBackgroundImage('"+selector+"')", anim_time);
}
// this is just a helper function to create php like random number generator
Math.rand = function (min, max) {
 return(Math.round(Math.random() * (max-min)) + min);
}


As it turned out, this was too much for the little machine..


2. attempt: with simple images
I don't want to make this post too long so I summarize it briefly. It doesn't went well. It was a little bit faster, but I needed much more.

3. attempt: Javascript Canvas
This has rocked my world again. After I have read Zsolti's article about canvas animations (thank you my dear friend :) ) I was very excited to test it on the real thing (as I was developing on my laptop). Needless to say.. it went perfectly.

Here is the code:

HTML:
<canvas width="1680" height="1050" id="index_canvas"></canvas>

Javascript:
// animation object
var indexAnim = {
 images: [], // here are the image objects
 positions: [], // the actual positions of the images
 destPositions: [] // the target coordinates of the image
}

jQuery(document).ready(function() {
 
 // init
 for( var i = 1; i<=4; i++) {
  
  // create a new image to place on canvas
  var im = new Image();
  im.src = 'images/index-bg-'+i+'.png';  
  indexAnim.images[i-1] = im;
  // place it randomly on canvas
  indexAnim.positions[i-1] = Array(Math.rand(-500, 500), Math.rand(-500, 500)); 
  // set it's destination positions
  indexAnim.destPositions[i-1] = Array(Math.rand(-500, 500), Math.rand( -500, 500));
 
 }
 
 // just for a better performance
 var length = indexAnim.images.length;
 var canvas = document.getElementById('index_canvas').getContext('2d');
 
 // let the animation begin (... WHA-HA-HAAA)
 setInterval(function () {
  
  // clear the image
  canvas.clearRect(0,0,1680,1050);  
  
  // let's redraw all the images
  for( var i = 0; i<length; i++ ) {
   
   // if a new target position is needed (either x or y)
   if( Math.abs(indexAnim.positions[i][0] - indexAnim.destPositions[i][0]) < i+1 || Math.abs(indexAnim.positions[i][1] - indexAnim.destPositions[i][1]) < i+1 ) {
    indexAnim.destPositions[i] = Array(Math.rand(0, 1050), Math.rand( -500, 500));
   }
   
   // set the new position of the image. It's only 1 pixel closer to the destination coordinate (Just like in life...)
   indexAnim.positions[i][0] = indexAnim.positions[i][0] + (indexAnim.positions[i][0] < indexAnim.destPositions[i][0] ? 1: -1);
   indexAnim.positions[i][1] = indexAnim.positions[i][1] + (indexAnim.positions[i][1] < indexAnim.destPositions[i][1] ? 1: -1);
   
   // draw image on canvas
   canvas.drawImage(indexAnim.images[i], indexAnim.positions[i][0],indexAnim.positions[i][1]);  
  
  }
 
  
 }, 41); // 41ms = 1000/24. 24 Frames are needed at least to create a smooth flow..
 
});

Math.rand = function (min, max) {
 return(Math.round(Math.random() * (max-min)) + min);
}


Maybe the animation needs some fine-tuning, but It's quite good :)

4. possible attempt: Flash
But since I don't do Flash programming, this solution depends on my brother :)


I hope you like my little solution here, and if you have a better idea share it with us.

No comments:

Post a Comment