Creating a 3D carousel from scratch

Using Raphael

3D Carousels have always been dominated by Flash but with the recent arrival of javascript animation libraries like Raphael and Processing Js, it is now possible to create effects that in the past were limited to flash only. In this tutorial with the magic of maths and javascript we are going to create an awesome 3D carousel, so lets get started !!!

Difficulty – Intermediate

View Demo

  • Step 1. We start off by downloading the Raphael Js library from Raphael Website and we will be using bit of jquery( you can use core javascript code for that),you can get that from jQuery Website.

  • Step 2.This will be our project structure.

    img1

    1) Index.html is our main html file.
    2) We will put Raphael.js in the js folder.

  • Step 3. Start off by creating the index.html file in the project directory and type in the following code -

    <html>
    <head>
    <style>
    #pane
    {
    background:#000000;
    }
    
    #logger
    {
    height:200px;
    width:100%;
    overflow:scroll;
    }
    
    </style>
    
    <link rel="stylesheet" type="text/css" href="css/style.css" />
    <script type="text/javascript"  src="js/jquery.js"></script>
    <script type="text/javascript"  src="js/raphael-min.js"></script>
    <script type="text/javascript"  src="js/carousel.js"></script>
    <title>3D Carousel</title>
    </head>
    
    <body>
    <div id="pane">
    
    <input type ="button" value="Previous" onclick="previous()" />
    <input type ="button"  value="Next" onclick="next()" />
    
    </div>
    <div id="logger">Logger</div>
    
    </body>
    </html>
    

    This is a simple HTML code in which we are going to create a div that will act as the stage for the carousel and a logger that will output our calculations for debugging purpose.

  • Step 4. Now before going further we are going to look at the logic involved.

    1. To give the illusion of a 3D carousel on a 2D plane we need to create the following effects-
      1. The foremost image should have the highest opacity and scale.
      2. The images should move in an elliptical path.
      3. As images move in clockwise or anti-clockwise path, the image moving further from the eyes should keep on decreasing its opacity and scale upto a point where the furthest image has the least opacity and scale.

      4. Now after that point the cycle reverses and as the furthest image starts to come at the front its opacity and scale starts to increase upto to a point where the image retains its original attributes.

      img2
    2. Now we will look into the variables that will define the above effect.

                   // these variable decide the position at the beginning
      var y=0;
      var radius=100;
      var xcenter=70;
      var ycenter=50;
      var zcenter=100;
      var radius_y = 60;
      // Used for scaling factor.
      // Basically it will work like a camera at Z axis.
      var fl=100;
      var shift_v = Math.PI / 2;
      // placement of each image at PI/3 distance,because of 6 images.
      var image_gap = Math.PI / 3;
      var angle;
      var angle_rad=angle * Math.PI / 180;
      
      img3
  • Step 5. Now after that we have understood the working of carousel, its time to start coding the actual code. We will create a carousel.js file in the js folder.

  • Step 6. Now we are going to use the concept of classes in javascript to create an image object since each image has its characteristics.

  • Step 7. Creating classes in javascript are bit different than the way we create classes in C++ or Java. It’s really easy. To declare a class we will use

    function MyClass()
    {
    
    } 
    

    This is same as function declaration, to create a constructor we will pass the parameters in the MyClass()
    and the code looks like

    function MyClass(var1,var2,..)
    {
    }
    

    Now class variables and functions are bit different to create, to create a variable we will use this keyword.
    For example to create a variable myVar1 we will type

    
    this.myVar1;
    

    and to create a function we will type

    this.myFunc = function(){
    }
    

    Now our overall class looks like

    function MyClass(var1)
    {
    this.myvar = var1;
    this.myFunc = function()
    {
    alert(this.myvar);
    }
    }
    

    That’s it we have just created a class in javascript language. To instantiate all we have to do is

    var obj = MyClass(“Hello”);

    and to call the method

    obj.myFunc();

    If you want to go in more detail regarding this, NETTUTS has an awesome article regarding Objects Oriented Javascript.

  • Step 8. Now getting back to our carousel, we will start off by declaring the following global variables.

    var y=0;
    
    var radius=100;
    var xcenter=180;
    var ycenter=50;
    var zcenter=100;
    var radius_y = 60;
    var fl=100;
    var shift_v = Math.PI / 2;
    var image_gap = Math.PI / 3;
    var angle=0;
    var angle_rad=angle * Math.PI / 180;
    
    var image_ar = new Array("img/im1.PNG","img/im2.PNG","img/im3.PNG","img/im4.PNG","img/im5.PNG","img/im6.PNG");
    var obj; // array in which we will store our carousel objects.
    

    Here most of the variables have been mentioned earlier except for the angle which is used for calculation for the coordinates , image_ar which is the array for holding image file path and obj which we will use later for storing our carousel objects.

  • Step 9. Now we are going to create the following class with a constructor that takes the following basic attributes for our carousel image object.

    function Carousel(x,y,z,scale,imge)
    {
    }
    

    Here x,y,z are the coordinates, scale is for the opacity and dimension purpose and imge refers to the path of the image.

  • Step 10. Now we will declare the follow data members for our class

    this.X = x;
    this.Y = y;
    this.Z = z;
    this.img = imge;
    this.scale = scale;
    this.r_image = r.image(imge,this.X,this.Y,100,100);
    

    X,Y,Z refers to each objects coordinates and r_image is the Raphael image object which is created using image function with parameters such as image path, coordinates and size. r is the global Raphael object which we will declare later.

  • Step 11. Now we need to declare two functions for our class

    1. To setup the initial attributes such as scale, opacity etc.
    2. To update the objects attributes to its new place when an event occurs.
  • Step 12. We will first create the create() function that will initialize the carousel objects. create() function consists of the following code

    
    this.create = function()
       {
    	 with (this)  
    	 this.r_image.scale(scale,scale);
    	this.r_image.animate({opacity:scale},1000);
       } 
    

    Here we need to specify only its scale and opacity since the coordinates and size has already been specified when the Raphael image object was created. You might be wandering what the with(this) code is for, it is used to refer to the current object in the function.

  • Step 13. Now we move on to create the update function that will calculate and change the objects attributes to it’s new position. The function consists of the following code.
    this.update = function(angle,i) 
      {
    	 
    	with (this)
    	 this.X= Math.cos(angle + i  *  image_gap + shift_v) * radius + xcenter ;
    	 this.Z=Math.sin(angle + i  *  image_gap + shift_v) * radius + zcenter ;
    	 this.scale = fl / (fl + this.Z);
         this.Y= -Math .sin(angle + i  *  image_gap + shift_v) * radius_y + ycenter ;
    	 with(this)
    	 $("#logger").append("<br> Image "+img+" Scale "+scale);
    	  this.r_image.animate({x:this.X,y:this.Y,opacity:this.scale,scale:this.scale+' '+this.scale},15);
    	
    	   
       }
    

    This function takes angle and i which stands for the image index which we will use later. Now to create an elliptical path we will use the above formula for each coordinates. We are using trigonometric equations because of the nature of the path we want. Here we also append the scale value in the logger div. Finally we use the animate function to move the object to its new attributes. Here we took time for the animation to be 15 ms, this seems to be very short but upon looking the big picture this animates only over a short distance and increasing this time would make the whole animation much more longer to complete.

  • Step 14. So finally our class code looks like -

    function Carousel(x,y,z,scale,imge)
    {
        this.X = x;
        this.Y = y;
        this.Z = z;
        this.img = imge;
        this.scale = scale;
    	this.r_image = r.image(imge,this.X,this.Y,100,100);
    	this.create = function()
       {
    	 with (this)  
    	  
    	
    	 
    	 this.r_image.scale(scale,scale);
    	this.r_image.animate({opacity:scale},1000);
       }
    
         this.update = function(angle,i) 
      {
    	 
    	with (this)
    	  var tempx = this.X;
    	  var tempy = this.Y;
    	 this.X= Math.cos(angle + i  *  image_gap + shift_v) * radius + xcenter ;
    	 this.Z=Math.sin(angle + i  *  image_gap + shift_v) * radius + zcenter ;
    	 this.scale = fl / (fl + this.Z);
         this.Y= -Math .sin(angle + i  *  image_gap + shift_v) * radius_y + ycenter ;
    	 with(this)
    	$("#logger").append("<br> Image "+img+" Scale "+scale);
    	  	  this.r_image.animate({x:this.X,y:this.Y,opacity:this.scale,scale:this.scale+' '+this.scale},15);
    	
    	   
       }
    
    
    }
    
    
  • Step 15. Now we will code the initialization part , we will define it in a anonymous function -

    $(function(){
     W = 800; H = 400;
      r = Raphael("pane", W, H);
    
    var i =0;
    obj = new Array();
    for(i=0;i<image_ar.length;i++)
    	{
    		var x1 = Math.cos(angle + i * image_gap + shift_v ) * radius + xcenter ;
    var y1 = -Math.sin(angle + i * image_gap + shift_v ) * radius_y + ycenter;
    var z = Math.sin(angle + i * image_gap + shift_v ) * radius + zcenter;
    var scale = fl/(fl + Math.sin(angle +i * image_gap + shift_v) * radius + zcenter);
    
         obj[i] = new Carousel(x1,y1,z,scale,image_ar[i]);
    		obj[i].create();
    	 
    	}
    
    });
    

    So when the DOM gets loaded we will create a global Raphael object with 800 width and 400 height which makes our pane div a canvas. Now We will create 6 carousels objects on the plane and to keep track of them we will store them in an array obj which we have declared in the global namespace. In the for loop we create the Carousel objects with their proper attributes and call the create() function to set the scale and opacity attributes. Now it looks like a 3D carousel.

    img5
  • Step 16. Now we require to have an elliptical motion when event occurs so we are going to create functions next() and previous() which will take care of the clockwise and anti clockwise motion for our carousel.

    function next(){ 
    angle_rad = angle_rad + Math.PI / 60;
      var i;
    	 for(i=0;i<image_ar.length;i++)
    	 {
    		 obj[i].update(angle_rad,i);
    	 }
    
     }
    
    

    We will update all the objects of the carousel to their new values. Since we need motion in clockwise direction we will add PI/60 to the angle variable. Similarly for previous function we will subtract PI/60 from angle and update all objects.

    function previous()
     { 
     angle_rad = angle_rad - Math.PI / 60;
      var i;
    	 for(i=0;i<image_ar.length;i++)
    	 {
    		 obj[i].update(angle_rad,i);
    	 }
    	 
     
     }
    

    But there is a problem with these functions that the displacement is so small we will barely notice it, we need keyframes similarly like flash for our functions for consistent animation. So we are going to wrap it in setIntveral() function and repeat it specified times. So now our functions looks like.

    function next()
     { 
     var count =0;
    var t = setInterval(function(){
      angle_rad = angle_rad + Math.PI / 60;
      var i;
    	 for(i=0;i<image_ar.length;i++)
    	 {
    		 obj[i].update(angle_rad,i);
    	 }
    	 
    	 count++;
    	 if(count==20)
    	 clearInterval(t);
    	  },50);
      
     }
    
    
    function previous()
     { 
     var count =0;
      var t = setInterval(function(){
      angle_rad = angle_rad - Math.PI / 60;
      var i;
    	 for(i=0;i<image_ar.length;i++)
    	 {
    		 obj[i].update(angle_rad,i);
    	 }
    	 
    	 count++;
    	 if(count==20)
    	 clearInterval(t);
    		  },50);
    	 
    
     
     }
    
  • Step 17. Now our complete code looks like -

    var y=0;
    var radius=100;
    var xcenter=180;
    var ycenter=50;
    var zcenter=100;
    var radius_y = 60;
    var fl=100;
    var shift_v = Math.PI / 2;
    var image_gap = Math.PI / 3;
    var angle=0;
    var angle_rad=angle * Math.PI / 180;
    
    var image_ar = new Array("img/im1.PNG","img/im2.PNG","img/im3.PNG","img/im4.PNG","img/im5.PNG","img/im6.PNG");
    var obj;
    
    function Carousel(x,y,z,scale,imge)
    {
    	this.X = x;
        this.Y = y;
        this.Z = z;
        this.img = imge;
        this.scale = scale;
    	this.r_image = r.image(imge,this.X,this.Y,100,100);
    	this.create = function()
       {
    	 with (this)  
    	 this.r_image.scale(scale,scale);
    	 this.r_image.animate({opacity:scale},1000);
       }
    
         this.update = function(angle,i) 
       {
    	 
    	with (this)
    	var tempx = this.X;
    	var tempy = this.Y;
    	this.X= Math.cos(angle + i  *  image_gap + shift_v) * radius + xcenter ;
    	this.Z=Math.sin(angle + i  *  image_gap + shift_v) * radius + zcenter ;
    	this.scale = fl / (fl + this.Z);
        this.Y= -Math .sin(angle + i  *  image_gap + shift_v) * radius_y + ycenter ;
    	with(this)
    	$("#logger").append("<br> Image "+img+" Scale "+scale);
        this.r_image.animate({x:this.X,y:this.Y,opacity:this.scale,scale:this.scale+' '+this.scale},15);
    		   
       }
    
      
    }
    
    function next()
     { 
     var count =0;
      var t = setInterval(function(){
       angle_rad = angle_rad + Math.PI / 60;
       var i;
    	 for(i=0;i<image_ar.length;i++)
    	 {
    		 obj[i].update(angle_rad,i);
    	 }
    		 count++;
    	 if(count==20)
    	 clearInterval(t);
    	 
       },50);
    
     }
    
    
    function previous()
     { 
     var count =0;
     var t = setInterval(function(){
     angle_rad = angle_rad - Math.PI / 60;
     var i;
      for(i=0;i<image_ar.length;i++)
    	 {
    		 obj[i].update(angle_rad,i);
    	 }
          count++;
    	if(count==20)
    	 clearInterval(t);
      },50);
    	 
    
     }
    
    
    
    $(function(){
      W = 800; H = 400;
      r = Raphael("pane", W, H);
    
      var i =0;
      obj = new Array();
       for(i=0;i<image_ar.length;i++)
    	{
    		var x1 = Math.cos(angle + i * image_gap + shift_v ) * radius + xcenter ;
    		var y1 = -Math.sin(angle + i * image_gap + shift_v ) * radius_y + ycenter;
    		var z = Math.sin(angle + i * image_gap + shift_v ) * radius + zcenter;
    		var scale = fl/(fl + Math.sin(angle +i * image_gap + shift_v) * radius + zcenter);
            obj[i] = new Carousel(x1,y1,z,scale,image_ar[i]);
    		obj[i].create();
    	 
    	}
    
    }); 
    
    	  
     })
    
  • Step 18. We are finally done:), for IE users animate() function does not work it can be overcome by using translate() method.

    img4

With a little tweaking you can use it in website header, ads and by attaching events they can be used with lightbox too.

Be Sociable, Share!

Written by Abhin Sharma

Abhin Sharma - I am web developer from Udaipur (city of lakes) ,India who loves to work with Java, Mashups and sometimes PHP. I specialize in Swings and JEE. While not programming I love to hang out my friends.

 

16 Responses to “Creating a 3D carousel from scratch”

  1. Rahul Says:

    November 17th, 2009 at 8:48 am

    Cool :)
    A demo would have been better

  2. Abhin Says:

    November 17th, 2009 at 8:56 am

    Here’s a working demo –
    http://webaura.1free.ws/carousel/

  3. Muhammad Adnan Says:

    November 17th, 2009 at 10:08 am

    nice ..

  4. drewdouglass Says:

    November 17th, 2009 at 7:48 pm

    Great work Abhin! I love to see things that would usually require a plugin or flash done by hand instead. Keep it up!

  5. Scentsy - Katie Jones Says:

    November 18th, 2009 at 1:40 am

    I love carousels but always wondered how they worked. Thank you for the info and also thank you to the person who posted a link for an example.

  6. Abhin Says:

    November 18th, 2009 at 1:42 am

    thanks :)

  7. Ben Says:

    November 18th, 2009 at 8:17 am

    Nice tutorial, I like trying to do things myself before using a plugin. It’s a good learning curve. I also usually find that it’s not actually that hard.

  8. Destillat KW49-2009 | duetsch.info - GNU/Linux, Open Source, Softwareentwicklung, Selbstmanagement, Vim ... Says:

    December 4th, 2009 at 2:48 am

    [...] Creating a 3D carousel from scratch [...]

  9. Really Useful Tutorials You Should Have Read in November 2009 Ajax Help W3C Tag Says:

    December 10th, 2009 at 1:36 am

    [...] Creating a 3D carousel from scratch By Abhin Sharma, November 17th, 2009 Site: Brenelz Web Solutions [...]

  10. Tutorial Lounge Says:

    December 31st, 2009 at 12:09 am

    excellent work you did in whole tutorial, helping for developers.

  11. Sean Radak Says:

    June 1st, 2010 at 10:39 am

    One thing that I’ve noticed is that upon rotating the carousel will shift the elements and will have an offset from the initialized location. I’ve been diving through the code but have not been able to find what causes this. any answers?

  12. hermes kelly bag Says:

    July 1st, 2010 at 11:49 pm

    One thing that I’ve noticed is that upon rotating the carousel will shift the elements and will have an offset from the initialized location. I’ve been diving through the code but have not been able to find what causes this. any answers?

  13. FlComponents Says:

    November 8th, 2010 at 5:31 pm

    Great tutorial. After some improvements I would like to obtain the same effect like 3d gallery

  14. vj Says:

    October 17th, 2011 at 5:26 pm

    The example web site seems to be broken. When I copied the code and tried it. The images appear and rotate like a carousel but the sizes do not change. The first image which is big and in front moves to the next location, but the size of all images remain the same.

  15. asm86 Says:

    December 27th, 2011 at 1:29 pm

    my 2ct

    line 43:
    this.r_image.animate({x:this.X,y:this.Y,opacity:this.scale,transform:’s'+this.scale},15);

    line 103:
    remove })

    Great example thanks !!

  16. saikat saha Says:

    October 3rd, 2012 at 10:17 am

    Is there any process to make this rotation automatic? Imean to say, I dont want to put previous and next buttons. Thanks in advance…

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

 
connect with me!