Let’s face it, the mere concept of doing something in 3D in the browser is pretty awesome. For those of us that remember the good old days of font tags and spacer gifs… well, let’s just say we’ve come a long way.
CSS3 3D transforms were recently added in Firefox 10 and IE10’s 3rd platform preview. They’ve been in Safari since v4, in iOS since v3.2, and they’re also supported in Android’s recent Ice Cream Sandwich release. So a decent chunk of web users now have the capability to run 3D transitions (at the time of this writing caniuse.com has it at ~32%).
So I decided I wanted to start messing around with this.
Note: Google Chrome supports 3D transforms, but Modernizr will report a false positive if Chrome has disabled GPU acceleration. So if you’re using Chrome you may or may not see a warning at the top of the screen saying that the demos won’t work (since I use Modernizr to detect whether to display that warning). I’ve also found that if Chrome does disable hardware acceleration, some of the demos in this post are a bit choppy, and occasionally don’t work. That’s one of the downsides with working something so new, all the kinks haven’t been worked out quite yet.
There are a few resources that I would recommend for getting started.
- An Introduction to CSS 3D Transforms by David DeSandro. This is an excellent starting point to learn the syntax and to see some pretty sweet demos.
- CSS 3D cube with touch gestures, click and drag by Paul Hayes. Another excellent demo of a 3D cube that supports the keyboard, touch gestures, and dragging.
- Mozilla Developer Network’s entry on CSS Transforms
The markup I ended up using was very similar to the cube in David DeSandro’s cube demo.
1 2 3 4 5 6 7 8 9 10
I’m not going to go into the full details of how the styling to create the cubes works, those are best covered by the articles I linked to earlier, but I did want to give a high level overview of what’s going on. Here’s a simplified version of the styles applied to each of the sides of the cube.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
The front side doesn’t need to be rotated since it’s already facing the screen front and center, but all the other sides need to be rotated on either the X or Y axis to create the cube. For example, the back side is rotated on the X axis -180 degrees so that it faces directly away from the screen.
Since this is hard to visualize in your head, here’s an example of a box rotating from 0deg to -180deg on the X axis in slow motion.
And here’s one going from 0deg to 90deg on the Y axis.
Although I won’t get into how the Z axis is used here, it’s perhaps the hardest to visualize so I thought I’d include an example of it as well. In 3D world it’s the axis that’s going straight between you and your monitor. Here’s an image going from -50px to 50px on the Z axis.
Rotating the Cube
So the 6 sides themselves have now have been rotated to form the cube. In order to show various sides of the cube to the user, the cube itself needs to be rotated to move the appropriate side to the front. This will be done by applying classes to the cube, one for each side.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Designing an API
I started with a constructor function that takes the necessary information, a DOM node and an array of the URLs for the images to use on the cube.
1 2 3 4 5 6 7
For convenience’s sake I allow the user to pass in either a String id attribute of a DOM node or the node itself. A quick conversion internally will make it so I only have to deal with the node.
Next you need to be able to do things with the cube. The things I wanted to do were the ability to go to a particular side, cycle through the sides at some interval, and the ability to clear that interval.
1 2 3 4 5 6 7 8 9 10 11 12 13
So to create the cube you call the constructor with the node you want the cube to be in and the images you want to be on the various sides of the cube.
1 2 3 4
Then you can simply call the methods provided.
1 2 3
What’s with the prototype?
The methods are added to the function’s prototype so PictureCube can be extended. As an example here’s an AwesomeCube extension that inherits functionality from PictureCube, and adds on an additional awesome method.
1 2 3 4 5 6 7 8 9
Line 1 defines AwesomeCube’s constructor function with the same parameters as PictureCube. It then invokes PictureCube’s constructor using function.apply. The apply function is defined in Function.prototype, and essentially allows you to control what the value of
this will be in the function being invoked. This technique is actually a common way of implementing Java-like super calls to chain constructors.
Why isn’t this a jQuery plugin?
jQuery is great for normalizing browser differences so you don’t need to worry about them. But in this case, the list of browsers that support CSS3 3D transformations have sufficient standards support that I had no need for jQuery. Therefore, writing this as a jQuery plugin would add an unnecessary dependency on jQuery.
Plus, jQuery plugins require some extra boilerplate to maintain state, which is necessary in this case. Using jQuery UI’s widget factory would work great, and might be something I consider if this implementation becomes more complex in the future. But for now didn’t want to add dependencies.
The current biggest limitation of the PictureCube implementation is that it has a hardcoded 100px height and 100px width. Eventually I plan on moving these declarations from CSS to JS so that it can be adjusted on the fly.
Putting it all Together
PictureCube.js is available to be forked or used on Github - https://github.com/tjvantoll/PictureCube. I’m planning on adding the ability to alter the height and width of the cube in a future update. Please give it a shot and let me know what you think.
Here’s a final cube for you to play with. Happy hacking.