eleqtriq

I finally found some time to finish my short series of tutorials on CSS 3D transforms. If you haven't checked out part 1 & 2 I recommend you to do so, otherwise you will feel lost pretty soon.

In this tutorial we will learn how to build a 3D packshot in HTML and CSS by applying some CSS 3D-transforms. Then we will add some Javascript to make the object freely rotatable in 3d space. And as we will enhance our Javascript with some touch-interactivity, the packshot will also work nicely in Safari for iOS-platforms like iPhone or iPad. A warning: be prepared that the second part of this tutorial will be quite math-heavy.

Open Example

...or direct your mobile-browser to www.eleqtriq.com/wp-content/static/demos/2010/rotation/ (sorry for the mobile-unfriendly URL).

The current State of CSS 3D

Since I published my first tutorial a few months ago, more and more sites showed up that actually use CSS3D transforms. But as we all have learned, support for CSS3D-transforms is still marginal outside the iOS world. So what is the use in learning this technique anyway?

Enough said, let's start the tutorial:

1. Building the packshot with HTML and CSS:

To demonstrate CSS 3D on a real world-example we will create a virtual packshot for Spritebaker. We start with a basic HTML-"sceleton":

<!-- We need something to apply some CSS 3D-perspective to it. Therefore we set up a section as our stage. --> <section id="viewport"> <!-- The next container is our box. It consists of 6 sections for every side: --> <article id="box"> <section id="front"> <!-- HTML for the front goes here--> </section> <section id="top"> <!-- HTML for the top goes here--> </section> <section id="bottom"> <!-- HTML for the bottom goes here--> </section> <section id="back"> <!-- HTML for the back goes here--> </section> <section id="left"> <!-- HTML for the left-side goes here--> </section;> <section id="right"> <!-- HTML for the inner-side goes here--> <!-- Just kidding :)--> </section> </article> </section>

In order to style the content we will use a handful of conventional HTML and CSS-techniques, therefore I will not bore you with too much details, you are a gown-up web-developer, right? Some lame 2005 image-replacement and CSS-sprites to make everything fairly accessible and crawlable (as you know I'm a big proponent of Data-URI-Sprites, but I avoid them in my demos most of the time to keep source-code readable).

2. Applying the CSS 3D Transforms:

More fun to come: let's form a nice 3D-box by rotating and positioning every side. The CSS transforms are straightforward and easy to understand:

section#viewport{ perspective: 700px; perspective-origin: 50% 50%; }article#box{ transform-style: preserve-3d; }#box section{ transform-style: flat; /*browsers are bitches at z-sorting css3d-transforms. Use backface-visibility: hidden as often as you can! */ backface-visibility: hidden; }#front, #back{ width:250px; height: 354px; transform: translate3d(0px, 0px, 37px); }#back{ transform: rotateY(180deg) translate3d(0px, 0px, 37px); }#top, #bottom{ width:250px; height: 74px; transform: rotateX(90deg) translate3d(0px, 0px, 37px); }#bottom{ transform: rotateX(-90deg) translate3d(0px, 0px, 317px); }#left, #right{ width: 74px; height: 354px; transform: rotateY(-90deg) translate3d(0px, 0px, 37px); }#right{ transform: rotateY(90deg) translate3d(0px, 0px, 213px); }

We add some spice by adding a webkit-box-reflect below and voila, ready is our box! Attentive readers will see that the reflection is erroneous, with the box-shape accurately reflected but not the typography on the box. This is a bug in webkit for MacOS. Applying a 3D-transform to an element causes that its content can't be reflected properly (strange to say this bug doesn't affect Safari for iOS).

3. Making the Box rotatable:

Figure 1: the Virtual trackball.

It's starting to get interesting: let's add some interactivity with Javascript so we are able to twist and turn the box with the mouse (side-note: we will avoid any Javascript-library like jQuery or Motools and code directly for Webkit instead, as its our sole target-browser. This way the script stays lean and small, <3kb gzipped without comments, mobile browsers will love you for that).

Some Words about Object-Rotation in 3D-Space:

Rotating and twisting objects in 3D-space might not sound like a big deal: just change the object's angle according to the mouse position on the screen, right? Unfortunately it's not that easy. The movement will feel unnatural and "bumpy" and we will have to deal with the problem of "Gimbal-Lock".

There exists a better technique for achieving a natural and smooth experience for the user. It's called the "Virtual Trackball". The Virtual Trackball is an imaginary sphere around our 3D-object. Every mouse-click on the screen will be mapped onto this virtual sphere and every dragging-operation will cause a rotation of the virtual trackball and the object inside, resulting in a smooth and intuitive rotation. Let's see how to build a virtual trackball for our box:

Mathematics of the Virtual Trackball:

How to construct the virtual trackball:

Figure 2: Mapping the coordinates of the mouse to the Virtual Trackball.

The center of the box and the Sphere must be at the center of the coordinate-system (y=0, y=0, z=0), which is also the center of the stage. The sole purpose of the virtual trackball is calculating angles in 3d-space. We are not interested in distances, it is enough to know their relation. Therefore we will put the sphere into an easy to calculate coordinate-system where its radius is simply "1". Mouse-coordinates on screen can be translated to this new coordinate system easily by dividing all values by the "untranslated" radius. Choosing the right radius is a matter of instinct. Choose a value that "feels right". I decided to make it half of the shortest side of the viewport. This maybe not o. k. in every situation though, you will always have to test and make your decision accordingly.

2. On mouse down, detect the mouse-coordinates and map them onto the trackball:

Figure 3: determining the rotation-axis from the vector at the moment of mousedown and the vector during rotation.

we translate screen-coordinates to trackball coordinates:

sphereX*2=mouseX/radius
sphereY*2=mouseY/radius

..and then move [0,0] to the center of the trackball coordinate-system:

x = x - 1;
y = y - 1;

We already know the radius of our sphere (it's 1, remember?) so it's quite easy to determine the z-coordinate of the translated point with the help of some good old trigonometry:

2*Z2=1-x2-y2

Now that we have everything in place, we can start building the rest of the logic. I will give an overview in pseudocode. One more remark: the script makes heavy use of 3d transformation-matrices and rotate3D, but avoids "rotateX, Y, Z". If we would use single-axis-rotations it would be necessary to calculate the rotation in quaternion-space to avoid gimbal-lock. If we use rotate3D or matrix3d, webkit will handle the quaternion stuff for us.

At first-run or at the moment the user releases the mouse (or lifts his hand) we create a new matrix3D from the last axis-vector and angle, store it in a variable and apply it to our box. When the user decides to click and rotate again, we will apply this startmatrix together with rotate3D to our box. This way we prevent our box from flipping back to it's initial, "unrotated" state on every mousedown/touchstart-event.

function init(){ set up the virtual trackball; search the viewport-html-element for something to rotate; add mousedown- or touchstart-listener to prepare rotation; add mouseup- or touchend-listener to finish rotation; add mousemove- or touchmove-listener to rotate; calculate initial matrix3d from initial angle and rotation axis; } function startrotation(){ track click-position and translate it to trackball; store resulting 3d-vector in variable "mouseDownVect"; }function rotate(){ track current mouse-position and translate it to trackball; store resulting 3d-vector in variable "mouseMoveVect"; find rotation-axis by determining normal on mouseDownVect and mouseMoveVect; find rotation-angle between mouseMoveVect and mouseDownVect; apply startmatrix and rotate3d(axis, rotation-angle) to the box; }function finishrotation(){ calculate new matrix3d from last angle and rotation axis; combine the last start-matrix and this new matrx to a combined matrix by multiplication; make current matrix3d new start-matrix; }

This is an overview of the actual logic. If you want to dig deeper, you should check out the Javascript, I gave my best to comment every step.

The script (I call it "traqball.js" by the way) is released under MIT- and GPL-license and you are alllowed to use it in your projects as long as you stick to the terms of license. Implementation is easy:

Ok, we're through! Hope you enjoyed this little lesson. If you have used the script in your projects I would be more than glad if you send me a link.

Update updated traqball.js to version2.0. Source now is on Github. Read more here..

Trackback

42 Responses to “Natural Object-Rotation with CSS3 3D”

  1. Pingback by Tweets that mention Natural Object-Rotation with CSS3 3D | eleqtriq -- Topsy.com Fri Nov 5th, 2010, 22:38

    [...] This post was mentioned on Twitter by Benjamin De Cock, WebKitBits, Jan Oelze, Maximiliano Firtman, Matt Powell and others. Matt Powell said: RT @WebKitBits: RT @deaxon: Natural Object-Rotation with CSS3 3D http://dxn.cm/s4 Works great on iOS! [...]

  2. Pingback by Natural Object-Rotation with CSS3 3D | The Hostmaster's Blog :: Web Hosting Tutorials | cPanel Guides | UK Unlimited Domain Web Hosting Mon Nov 8th, 2010, 14:22

    [...] Read more at http://www.eleqtriq.com → [...]

  3. Comment by Jerome Thu Nov 11th, 2010, 14:19

    Nice article, thank you!
    Btw, the “Tweet me” link doesn’t work… (‘url’ parameter does not contain a valid URL.)

  4. Comment by Dirk Thu Nov 11th, 2010, 15:57

    Hi Jerome,
    I’m glad you liked it. I postponed to fix the IE problems with the tweetbutton for weeks now, but I will look into it tonight :-)

  5. Pingback by Serio. Te artykuły pomogą ci zostać nieziemskim webdesignerem :] : Jak Stworzyć Stronę Mon Nov 15th, 2010, 07:07

    [...] Natural Object-Rotation with CSS3 3D [...]

  6. Pingback by 10 New CSS3 Tutorials -December Wed Dec 1st, 2010, 14:36

    [...] Natural Object-Rotation With CSS3 3D In this tutorial by Dirk Weber you will learn how to build a 3D packshot in HTML and CSS by [...]

  7. Comment by Derek Tue Dec 7th, 2010, 01:00

    Chrome Dev. version can see the 3d effect! (9.0.597.0 dev)

  8. Comment by Benton Thu Dec 23rd, 2010, 19:31

    Hey this is wonderful,
    but I’m having issues with a syntax error in the java script in my head
    “set up virtual trackball” is a syntax error:

    function init(){
    set up the virtual trackball;

    Do I need to incorporate the traqball.js?
    Thank you So much. I’m sorry to bother you with novice Questions.

  9. Comment by Dirk Fri Dec 24th, 2010, 00:45

    Hi Benton,
    this is pseudocode, it can’t be executed. It’s purpose is to desribe the single steps in the process in plain english. If you want to use the technique on your site you must use trackball.js.

  10. Comment by Benton Fri Dec 24th, 2010, 01:40

    Ahhh
    The ole pseudocode trick ; )
    obviously while the English is plain, it still evades me. (I’m good with Css & Html, not with java)
    So would I use the traqball.js in the head with your code?
    I have your code working in my page.

    Thanks so much for your help Dirk. This is what I have been hoping for!

  11. Comment by Benton Fri Dec 24th, 2010, 01:52

    Ahhh
    Slowly it dawns on me.
    Your code is the plain English for what to do with the traqball.js
    I’m learning.
    Thanks for your inspiration!

  12. Comment by Dirk Fri Dec 24th, 2010, 12:45

    Almost :). Its intended to demonstrate what’s going on under the hood. For implementaton it is enough to pass a div, section, p, whatever to the “check-function, e.g: doonload.check(“someID”, [0,1,0], 0) and trackball.js will do the rest as long as the element contains some sub-element. Be prepared for browsers that can not handle CSS3 3d. Either prepare alternative content or degrade gracefully.

  13. Comment by Paul Irish Fri Mar 11th, 2011, 19:13

    So your browser sniff (which you knew and noted is bad) IS BAD. :)

    It’s false negative’ing on Chrome and Chromium. 3D transforms are landing in Chrome 11 stable are available in the dev channel now.

    You can use Modernizr for the feature detect. The trick it uses is that webkit exposes a media query as well when 3D transforms is enabled and that doesn’t lie, unlike backface-visibility.

    Cheers.

  14. Comment by Dirk Fri Mar 11th, 2011, 19:40

    Yep I know :). But in November, when I did this demo, Modernizr still returned true for 3D transforms for every webkit I tested. So this is an act of pure despair :). But i have planned to take some time to update the code in the near future.

  15. Comment by Mike Mon Mar 28th, 2011, 14:01

    Which version of Chrome is suppose to work on because I have the latest albeit a beta version but it went straight to the video and not the demo.

  16. Comment by Cristy Mon Mar 28th, 2011, 23:49

    The example isn’t working for me…. ( Chrome 12 )

  17. Comment by Sunny Singh Mon Mar 28th, 2011, 23:52

    Are you checking the browser or does Firefox 4 really not support this?

  18. Comment by Dirk Wed Mar 30th, 2011, 21:36

    es I’m checking browser. Unfortunately fox still doesn’t support CSS 3d transforms :(

  19. Comment by Dirk Wed Mar 30th, 2011, 21:37

    Mike, Christy – which platforms are you using?

  20. Comment by jessicapretty Mon Apr 4th, 2011, 23:12

    This is your best topic yet!

    http://en.wikipedia.org/

  21. Comment by waqas Sat Apr 23rd, 2011, 01:48

    Sigh, what I wouldn’t give to stop people from using stupid browser sniffing. Fails on chrome because Chrome versions are no longer single digit…

  22. Comment by Dirk Sun Apr 24th, 2011, 13:00

    Actually you have 3 options here:

    1. You are a smart kid. Shouldn’t be too hard for you to figure out how to set up the browser sniffing for current versions of chrome.

    2. Wait until I put a new version online. Should happen within the next 2 weeks if I find the time.

    3. Build a version that works without browser sniffing (which isn’t that easy, I can tell you) and post the solution on your blog or here. This is much more constructive than hanging around on other peoples blogs and proclaim. And as a side efffect the community will actually profit from your input. Read Paul’s comment above for an example how to give constructive feedback the right way.

  23. Comment by Derek Fri Jul 29th, 2011, 23:08

    How do you use the “traqball.js”? Can you make a tutorial of it?

  24. Comment by Derek Fri Jul 29th, 2011, 23:46

    I don’t really understand what should put in the [a,b,c] and the “initial rotation-angle”… Please help…

  25. Comment by Dirk Sat Jul 30th, 2011, 00:34

    Hi Derek,
    the first is an array representing the x-, y-, z-axis components, the last is an angle in radian. E.g. [0,1,0], 1.5707 will rotate the object 90deg around the y-axis. If you are undecided leave them out and default vals ([1,0,0], 0 ) will kick in.

  26. Comment by Derek Sat Jul 30th, 2011, 20:26

    @Dirk,
    Thank you for your explanation! And I spent some time, and I figured out that the “traqball.js” is not working if I use “margin:0 auto” to center the cube.

  27. Comment by Derek Sun Jul 31st, 2011, 02:32

    CORRECTION: Maybe it is not the “margin:0 auto” that causing it not working. I don’t know but when I add a “width” and “height” to the container, it started to work.
    http://derek1906.site50.net/experiment/css3d/

  28. Comment by ZhouQi Thu Aug 11th, 2011, 14:57

    it’s magic!

  29. Pingback by CSS Summit 2011 – Notes from Day 1 Sun Aug 14th, 2011, 07:29

    [...] Natural object rotations with CSS3 3D – very cool effect (click on image & use your mouse to move it around). [...]

  30. Comment by Javery Fri Oct 14th, 2011, 06:33

    You were just featured during the Web Directions 2011 in Sydney. You’ve got a room of over 100 people who are VERY impressed.

    Nice work!

  31. Pingback by New CSS Techniques and Tools | W Design Love | Graphic and Web Design Blog | Coding | tutorials | freebies | Wordpress | inspiration | Blog Sun Oct 16th, 2011, 21:40

    [...] Natural Object-Rotation with CSS3 3D A tutorial by Dirk Weber that teaches us how to build a 3D packshot in HTML and CSS by applying some CSS 3D-transforms. By adding some Javascript, we can make the object freely rotatable in 3D space. And as we will enhance our Javascript with some touch-interactivity, the packshot will also work nicely in Safari for iOS-platforms like iPhone or iPad. [...]

  32. Pingback by Weekly Update 4 | Justin Avery Web Site Analysis Design Development Sun Oct 23rd, 2011, 17:33

    [...] rotations…. it’s got most of what you might want (cause you never really need it) 3D Image transformation is one of the cool bits I learned while at the Web Directions South.  This is an awesome [...]

  33. Comment by Paul Irish Sat Nov 5th, 2011, 22:46

    Nice rewrite! Sexy conditional loading action. :)

    Unfortunately the new code has a bug. Looks like Chrome doesn’t like you not passing an argument to cancelRequestAnimationFrame().. I get TypeError: Not enough arguments

    Fix is easy enough. Capture the return value of rAF and then shoot it into your cRAF call. Already tested it out and it’s groovy. :)

    Btw we caught this because the demo broke onstage during @garazi’s talk at StarTechConf in santiago Chile. :p

  34. Comment by Dirk Mon Nov 7th, 2011, 14:24

    *cough* – how embarrrrrassing o_O. Should work again now :-) Thanks Paul!

  35. Pingback by Robert’s read: links and suggestions November 16th 2011 - Robert's talk Wed Nov 16th, 2011, 15:06

    [...] Natural Object-Rotation with CSS3 3D [...]

  36. Comment by Forkoff Fri Feb 3rd, 2012, 13:08

    Firefox now supports 3D transforms as of Version 10

    x

  37. Comment by Stefan Fri Mar 2nd, 2012, 14:00

    Hi there,

    We are currently using the cube and are trying to make the images clickable for browsers AND the iPad.
    It works for the browsers only not on the iPad, perhaps I’m doing it wrong. We’ve used the code:

    It seems the only thing clickable on the iPad is the borders, we’ve also removed the images but still you can’t click on the planes. It there anything that we can do to make the images clickable on the iPad?

    Cheers (from Holland)

  38. Comment by mauritius Mon Mar 19th, 2012, 05:32

    Awesome would really want to integrate this css 3d in one of my website but the browser compatibility issue really make this difficulf

  39. Pingback by GNC Designstudio Sat Jun 23rd, 2012, 15:27

    [...] Link: Dirk’s rotation example [...]

  40. Comment by Star Wed Jul 4th, 2012, 03:51

    the coolest thing i’ve seen in my life .I think this the future of web technology.But ie please support on ie7 as well as ie8.I hope in the near future all browsers will automatically updates.With or without the consent from the owner.that would be awesome.I will apply your ideas into my website.hahah
    hope you will find it amusing
    http://linesforme.blogspot.com

  41. Pingback by 35 Best HTML5 and CSS3 3D Examples with Demo Wed Aug 22nd, 2012, 07:05

    [...] Visit tutorial [...]

  42. Comment by Wolden Tue Oct 2nd, 2012, 18:13

    Hi :)

    Nice stuff & nice effect! I’m trying to connect your magic 3d box with the gyroscope datas of the pad (x,y,z) instead of finger position to obtain something like like a pseudo cam (the box seems to stay fixed in space keeping its right perspectives when the pad moves) but I cant understand how to do this because the movement is waiting for a touch on screen to be initiated. I suppose that it would be easy to remove the touch events and replace xTouch/yTouch by the gyro datas, but I’m afraid that the object-supposed-to-be-clicked will be hard to define? any idea ?