Friday, December 19, 2008

Javascript image effects, fade-in

It's been a long while since my last post, but recently I found some time to write some new scripts. These are object oriented, so they are easier to use and have very little (or no) issues with scope collisions and the like. From now on, I will try to write all examples using OOP and 'classes'. This script is used to apply effects on pictures in a page. The effect shown in it is "Fade-in" - the image starts fully transparent and smoothly appears till it's fully visible. This is just the first effect of the series, and that is all I will show for today, but there will be additional effects added constantly :) Here's what I've made:
//THE object constructor (class, etc.)
function imageEffects(elmnt, pics, effectId, steps, time)
{
 //If the required parameters are not passed, show an error and stop.
 if(!elmnt || !pics)
 {
  alert('ERROR: imageEffects object not initialized properly!');
  
  return;
 } 
 
 //Assigning default values of the optional parameters
 if(!effectId)
  var effectId = 1;
 
 if(!steps)
  var steps = 50;
 
 if(!time)
  var time = 1000;
 
 
 this.elmnt = document.getElementById(elmnt); //html element (img) that will be manipulated
 this.pics = pics;          //an array with the pictures that will be shown
 this.steps = steps;         //number of steps for changing pictures
 this.effectId = effectId;       //id of the effect that will be applied. this is for future use
 this.effectTime = time;        //time that will be needed to change between pictures
 this.step = this.steps;        //used to control the smooth changing of the pictures
 this.pic = 0;           //current picture that is being shown
}


//Changes the opacity of any given element. cross browser.
imageEffects.prototype.setOpacity = function(opacity) 
{
   this.elmnt.opacity = (opacity / 100);
   this.elmnt.filter = "alpha(opacity="+opacity+")";
   this.elmnt.MozOpacity = (opacity / 100);
 this.elmnt.KhtmlOpacity = (opacity / 100);
}

//This prototype changes the pictures, handles steps, etc.
imageEffects.prototype.changePic = function(reverse)
{
 //If the previous picture hasn't finished showing, we need to wait
 if(this.step < this.steps)
  return;
 
 //If we got here, the previous picture has finished showing and the user now wants to change it. 
 //Set the step to 0 to start the effect again
 this.step = 0;
 
 if(reverse) //Show previous picture (or if this is the first one - show last)
 {
  if(this.pic == 0)
  {
   this.pic = this.pics.length-1;
  }
  else
   this.pic--;
 }
 else //Show next picture (or if this is the last one - show first)
 {
  if(this.pic == this.pics.length-1)
  {
   this.pic = 0;
  }
  else
   this.pic++;
 }
 
 
 //Change the image
 this.elmnt.src = this.pics[this.pic];
 
 //Determine the time interval that will be used based on the overal time and number of steps 
 var interval = this.effectTime/this.steps;
 
 for(i = 1; i < this.steps+1; i++)
 {
  //call the prototype that visualizes the effect
  setTimeout(methodize(this.setOpacity, this), i*interval);
 }
}


//The fade-in effect. Smoothly changes opacity of the picture from 0 to 100
imageEffects.prototype.setOpacity = function()
{
 //if we've reached the last step - stop the effect. Generally, this soudn't happen, cause it's 
 //controlled by the code that calls the effect, but it's an extra insurance. 
 if(this.step == this.steps)
    return;

 //Determine the opacity that the picture will have at this step.. 
 var opacity = this.step*(100/this.steps);

 //..and apply it to the image. 
 var element = this.elmnt.style; 
 
 element.opacity = (opacity / 100);
   element.filter = "alpha(opacity="+opacity+")";
   element.MozOpacity = (opacity / 100);
   element.KhtmlOpacity = (opacity / 100);
   
   //Go to the next step
   this.step++;
}

//This is a very simple, yet very powerful function that basically allows us to call objects from another 
//scope,in fact, any scope we want, which is not possible with a language construct or anything of the like..   
//There are still a lot of limitations in javascript and this is breaking one of them :)
//This is where I heard of it the first time I think - 
//http://www.tapper-ware.net/2006/12/object-oriented-programming-and-event.html. There are some additional 
//features in the example and I recomend you read it, but we just don't need them here. 
function methodize(methodize_func,methodize_scope)
{
    return (function() { methodize_func.call(methodize_scope); });
}
The html we need to use to get this working is just an image element and some kind of event trigger. We can use any of the event triggers that exist, but I think it's most appropriate to use either onclick or onmouseover. I've set an example page that shows three pictures. The first one is changed onclick. The second one is changed by clicking on the two arrows beneath it. The first arrow shows previous picture and the second one shows next. The last picture is changed onmouseover, and it changes in reverse/descending order. I've used a div and some other elements to position the elements on the page, but the code itself doesn't require anything else except an image. Here is the html:
<div style="width: 1024px; position: absolute; left:50%; margin-left: -512px; text-align: center">
<img id="img" src="1.jpg" style="width: 512px" onclick="test2.changePic(1);">
<br /><hr />
<img id="img2" src="2.jpg" style="width: 1024px"><br /><br />
<img id="l_arrow" class="arrow" src="lArrow.gif" onclick="test.changePic(1);">
     
<img id="r_arrow" class="arrow" src="rArrow.gif" onclick="test.changePic();">
<br /><hr />
<img id="img3" src="3.jpg" style="width: 1024px" onmouseover="test3.changePic();">
</div>
Now that we have the html, we need to create the imageEffects objects for every picture we want to manipulate. These are already used in the html and are named test, test2 and test3. Lets create the objects now:
//The first one will change the pictures for two seconds (2000 miliseconds)
//and will do it in 40 steps
var test = new imageEffects('img', new Array('1.jpg', '2.jpg', '3.jpg'), 1, 40,
2000);

//The second will use the default values for steps and time, which are -
//change the pictures for a second, in 50 steps.
//This one uses the same pictures, but in slightly different order.
var test2 = new imageEffects('img2', new Array('2.jpg', '3.jpg', '1.jpg'));

//The third one will change the pictures for 3 seconds in 100 steps
//This one uses the pictures in completely different order.
var test3 = new imageEffects('img3', new Array('3.jpg', '1.jpg', '2.jpg'), 1,
100, 3000);
This is pretty much it. I've put the code on one of my pages, so you can use it on your page using this code:
<script type="text/javascript" src="http://enikoloff.info/scripts/imageEffects.js"></script>
This will create the imageEffects class on your page. Then all you need to do is create some instances of that object (just as I did with test, test1 and test2) and call them with some event trigger. Don't forget to visit my site where you can find other examples on using this class and other free scripts :) P.S You can see the example from above here.