Creating a CSS3 3D image rotator

September 22nd, 2014

A recent client project called for an image rotator that was essentially a 3D cube. I was pretty excited when I heard that, because I had not really had much of a chance to use any of the CSS3 3D stuff on a project yet!

Lets take a walk through the process of creating this slider!

First off, below is an example of the final product.

See the Pen CSS 3D Image Rotator by Ryan Christiani (@Rchristiani) on CodePen.


It works as you would except. Click a dot, and the image that is related to that dot will rotate into view.

The concept is pretty simple. We have a container and in that container we another container that will actually hold our images.


<div class="container">
     <div class="image-area">
          ...
     </div>
</div>

We use the first container to set the perspective for this 3D object. This is done on our .container element.


.container {
	width: 400px;
	height: 300px;
	position: relative;
	margin: 50px auto;
	/*Set the perspective on the container*/
	-webkit-perspective: 800;
}

This is not actually the container that gets rotated however. But we must use the perspective property to get a 3D context for our child elements.

Next we have to apply a few styles to the section we will actually rotate, the .image-area element. In this case just a few basic styles and set up some initial 3D styles.


.container .image-area {
	height: 300px;
	width: 400px;
	opacity: 1;
	/*Rotate the secondary container*/
	-webkit-transform-style: preserve-3d;
	-webkit-transform: rotateX(0deg);
	transition: all 1s ease;
}

Since we will have 3-4 slides, we will need some elements inside our container to hold our content. I add three elements, each with class of item and then a class of first, second and third. This way we can position our items as faces of a 3D cube.


<div class="container">
     <div class="image-area">
        <div class="item first"></div>
        <div class="item second"></div>
        <div class="item third"></div>  
     </div>
</div>

Now that our items are added we need to actually position them in a cube layout. Lets add a little style to the item class:


.container .item {
	position: absolute;
	top: 0;
	left: 0;
    height: 300px;
	width: 400px;
    background-size: cover;
    background-position: center;
	-webkit-backface-visibility: hidden;
    backface-visibility: hidden;
}

I position the elements absolutely in the container and make sure they are at the top left corner, give them a height and a width and get background-size to cover so that when we add images they fill the whole space. The one thing to note here is the backface-visibility property. Setting this to hidden lets us rotate the element and have it no show the back of it, much like the property name suggests.

Now all we have to do is layout our faces. It is pretty straight forward. If you think about how the items are currently laid out, they are stacked on top of each other. What you we need to do is set these into a 3D space. We do that but rotating the faces on the x axis and pushing it away from the initial plane with translateZ(150px) which is half the height of the elements. Imagine if you cut out a cube template out of paper, and how you would fold it together.

To do this we need to apply the following to our items:


.container .item.first {
	-webkit-transform: rotateX(0deg) translateZ(150px);
	transform: rotateX(0deg) translateZ(150px);
}

For each new face, the second and third, you increase the rotateX value by 90 degrees. On each one of these items I also added a background image.

By now you should only see one element, the first one, and if you use the inspector you can see that the others are laid out and rotated.

All that is left is to add the function of clicking a dot and rotating the cube. This is also fairly straight forward, but lets take a second to look at the code. In this case I am using jQuery for simplicities sake in querying the DOM.

First up we should wrap all of our js in a document ready function, in this case I use the short hand:


$(function() {
    ...
});

We do this for a couple reasons. One reason being that we want document to be ready so we can get at elements on the page. The second reason being we do not want to pollute the global scope with our functions and variables. Inside here we will have two functions, the first will be a function to create our dots.


function createDots(cb) {
	var $slides = $('.image-area .item');
	var slideLength = $slides.length;
	var dotTemp = '
'; var html = ''; var $dotContainer = $('.dots'); for (var i = 0; i < slideLength; i++) { html += dotTemp; } $dotContainer.append(html); $dotContainer.find('.dot').eq(0).addClass('current'); cb(); }

This function takes one argument, which is another function used as a callback. It first gets a reference to the items, and then gets the length, or number of items on the page. We when set up a simple template for the dots and initialize a blank string to hold that html. We loop over the number of items and append a string to our blank html variable so that it will contain a div for each item in our cube. After that is done we simply append that to the container and set the first one to be the current. We then call our callback function, which is the function used to set up the events on the dots, lets take a look:


function rotateSlider() {
	var $container = $('.container .image-area');
	var $dot = $('.dots .dot');
	$dot.on('click', function() {
		var index = $dot.index($(this));
		$('.dots .dot').removeClass('current');
		$(this).addClass('current');
		$container.css('transform','rotateX(' + (index * 90) +'deg)');
		$container.addClass('transition');
		$container.on('transitionend', function() {
			$container.removeClass('transition');
		});
	});
}

We start by getting a reference to the container and then the dots themselves. Then we set up a click event on the dots. In this event we have access to the index of the dot clicked, is it the second or third dot, and we can use that to determine the rotation value for the container. This can be achieved simply by multiplying the index by 90.

And there you have it, to start it all off we can call the createDots() function and pass in a callback which calls our rotateSlider function:


createDots(function() {
	rotateSlider();
});

Take a look at the pen to see it all working together. There are a lot of really great links out there to get started with 3D CSS3, I think this one from Dave DeSandro is a really great primer, I highly suggest checking it out.