eleqtriq

Ich hatte die letzten Tage endlich mal Zeit den längst überfälligen 3. Teil meiner Serie über CSS3 3D-Transformationen zu zu beenden. Sollte Ihr Teil 1 & Teil 2 noch nicht gelesen haben, rate ich das kurz nachzuholen.

In diesem Tutorial werden wir uns damit befassen, wie man einen Packshot in HTML und CSS erstellt, indem man einige CSS3 3D-Transformationen anwendet. Anschließend fügen wir noch etwas Javascript hinzu und zaubern ein frei rotierbares Objekt. Da wir direkt noch die notwendigen Touch-Events hinzufügen werden wird das Ergebnis eine Box sein, die nicht nur in Safari-Desktop sondern auch auf dem iPhone und dem iPad dargestellt und bewegt werden kann. Eine kleine Warnung vorab: Teil 2 ist recht mathelastig.

Beispiel öffnen

...oder öffnet www.eleqtriq.com/wp-content/static/demos/2010/rotation/ direkt in eurem mobilen Browser (sorry für die mobilsuboptimale URL).

Wie ist der Stand von CSS3 3D?

Seit meinem letzten Tutorial vor einigen Monaten hat die Zahl der Internetseiten die CSS 3D verwenden weiter zugenommen. Aber wir wissen: CSS 3D-Transformationen werden außerhalb von iOS derzeit fast überhaupt nicht unterstützt. Warum sollte man sich mit dieser Technik also befassen?

Genug geplaudert, legen wir also los:

1. Wir erstellen einen Packshot mit HTML and CSS:

Um CSS 3D unter realistischen Bedingungen zu demonstrieren werden wir einen virtuellen Packshot für Spritebaker entwickeln. Den Anfang macht ein einfaches HTML-Gerüst:

<!-- Wir brauchen erstmal etwas dem wir eine CSS 3D-Perspective zuweisen können. Deshalb bestimmen wir eine <section>, die als Bühne fungiert. --> <section id="viewport"> <!-- Der nächste Container ist die eigentliche Box. Er ist in 6 Sections für die einzelnen Seiten gegliedert: --> <article id="box"> <section id="front"> <!-- HTML für die Vorderseite--> </section> <section id="top"> <!-- HTML für die Oberseite--> </section> <section id="bottom"> <!-- HTML für die Unterseite--> </section> <section id="back"> <!-- HTML für die Rückseite--> </section> <section id="left"> <!-- HTML für die linke Seite--> </section;> <section id="right"> <!-- HTML für die Innenseite hier--> <!-- Ok, blöder Witz.--> </section> </article> </section>

Den Inhalt stylen wir mit einer handvoll konventioneller HTML und CSS-Techniken, deswegen spar ich es mir hier, Euch mit den Details zu langweilen, Ihr seid schließlich Profis, oder? Ein paar langweilige 2005 Image-Replacements und CSS-Sprites um einiges einigermaßen transparent und crawlbar zu machen (Ihr wisst, dass ich ein großer Anhänger von Data-URI-Sprites bin, aber in meinen Demos vermeide ich sie in der Regel damit der Code lesbar bleibt).

2. Die CSS 3D Transformationen:

Langsam fängt's an Spaß zu machen: nun bauen wir uns eine hübsche CSS-Box indem wir jede Seite drehen und an die passende Stelle verschieben. Die CSS-Transformationen sind nicht weiter kompliziert und leicht nachvollziehbar:

section#viewport{ -webkit-perspective: 700px; -webkit-perspective-origin: 50% 50%; } article#box{ -webkit-transform-style: preserve-3d; } #box section{ -webkit-transform-style: flat; /*webkit is extrem zickig was das z-Sorting angeht. Nutzt backface-visibility: hidden wann immer Ihr könnt! */ -webkit-backface-visibility: hidden; } #front, #back{ width:250px; height: 354px; -webkit-transform: translate3d(0px, 0px, 37px); } #back{ -webkit-transform: rotateY(180deg) translate3d(0px, 0px, 37px); } #top, #bottom{ width:250px; height: 74px; -webkit-transform: rotateX(90deg) translate3d(0px, 0px, 37px); } #bottom{ -webkit-transform: rotateX(-90deg) translate3d(0px, 0px, 317px); } #left, #right{ width: 74px; height: 354px; -webkit-transform: rotateY(-90deg) translate3d(0px, 0px, 37px); } #right{ -webkit-transform: rotateY(90deg) translate3d(0px, 0px, 213px); }

Zum Abschluß noch ein kleiner webkit-box-reflect daruntergesetzt und voilá, die Box steht! Aufmerksame Leser werden bemerkt haben, dass die Reflektion falsch ist. Die Form der Box wird zwar korrekt gespiegelt, aber nicht die Typo darauf. Es handelt sich hier um einen Fehler in Webkit für MacOS. Wenn man eine 3D-Transformation auf ein Element anwendet, führt das dazu, dass ihr Inhalt nicht gespiegelt wird (ein Fehler, der im Safari für iOS merkwürdigerweise nicht auftritt).

3. Die Box rotierbar machen:

1: Der Virtuelle Trackball.

Nun wird's interessant: wir werden jetzt die Interaktivität mit Javascript hinzufügen, damit wir die Box mit der Maus frei rotieren können (nebenbei bemerkt: wir werden Javascript-Librarys wie jQuery oder Motools meiden und stattdessen direkt für Webkit coden, das ist ja unsere auschließliche Zielengine. Dadurch bleibt unser Script schlank und klein, <3kb gzipped, und der Mobilbrowser schlägt Purzelbäume vor Glück.

Ein paar Worte über Objektrotation im 3-dimensionalen Raum:

Objekte in drei Dimensionen zu rotien mag sich erstmal nach keiner großen Sache anhören: wir ändern einfach die Rotation abhängig von der Mausposition auf dem Bildschirm, oder? Leider ist das nicht so einfach. Die Bewegung wird sich unnatürlich und "holprig" anfühlen und wir werden dem Problem des "Gimbal-Lock" begegnen, welches auch die Spieleentwickler immer wieder zum Haareausreissen treibt.

Es gibt eine bessere Methode um eine natürliche, weiche Rotationsbewegung zu erzielen. Man nennt diese Technik den "Virtuellen Trackball". Es handelt sich dabei um eine virtuelle Kugel um das 3D-Objekt. Jeder Mausklick auf den Bildschirm wird auf diese virtuelle Kugel projiziert und jede Zugbewegung bewirkt eine Drehung des virtuellen Trackballs einschließlich des Objektes darinnen. Schauen wir mal, wie sich ein virtueller Trackball für unsere Box bauen lässt:

Die Mathematik des Virtuellen Trackball:

Wie man einen Virtuellen Trackball konstruiert:

2: Die Mauskoordinaten auf den Virtuellen Trackball mappen.

Der Mittelpunkt der Kugel und der Box müssen sich am Mittelpunkt des Koordinaten-Systems befinden (y=0, y=0, z=0), der auch den Mittelpunkt der Bühne darstellt. Da der einzige Sinn eines virtuellen Trackballes darin liegt, Winkel im Raum zu berechnen, sind uns Entfernungen und Strecken egal, es reicht ihr Verhältnis zueinander zu kennen. Wir übersetzen die Kugel deshalb in ein Korrdinatensystem mit dessen Maßen sich einfach rechnen läßt, in diesem Fall ein Koordinatensytem in dem der Radius einfach "1" beträgt. Die Maus-Koordinaten auf dem Bildschirm lassen sich leicht in das neue Koordinatensystem umrechnen indem wir alle Werte durch den ursprünglichen Radius teilen. Welchen Radius wir verwenden hängt von unserem Empfinden ab. Ich habe mich dafür entschieden, die Hälfte der kürzesten Seite des Viewport zu wählen, manchmal mag aber ein anderes Maß geeigneter sein.

2. Bei Mausdruck registrieren wir die Mauskoordinaten und mappen sie auf den Trackball:

3: Die Rotationsachse wird anhand des Vektors bei Mausdruck und während der Drehung bestimmt.

Wir übersetzen die Bühnenkoordinaten zu Trackball-Koordinaten...

kugelX*2=mausX/radius
kugelY*2=mausY/radius

...und bewegen anschließend den Mittelpunkt [0,0] zum Mittelpunkt des Trackball-Koordinatensystems:

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

Den Radius unserer Kugeln kennen wir ja bereits (Ihr erinnert Euch sicher, es war 1) somit ist es relativ einfach die z-Koordinate des übersetzten Punktes unter Zuhilfenahme von etwas guter alter Trigonometrie zu bestimmen:

2*Z2=1-x2-y2

Nachdem wir nun alles beieinander haben, können wir loslegen und die restliche Logik basteln. Ich gebe gleich einen Überblick in Pseudocode. Vorher noch eine Bemerkung: wir nutzen vorwiegend 3D-Transformationsmatrizen und Rotationen um 3D-Vektoren (rotation3d) aber wir vermeiden "rotateX, Y, Z". Würden wir Rotationen um einzelne Achsen einsetzen, müssten die Drehungen mittels Quaternionen berechnet werden um den erwähnten Gimbal-Lock zu vermeiden. Wenn wir rotate3D oder matrix3d verwenden nimmt Webkit uns die Sache mit den Quaternionen ab.

Nach Abschluss der Rotation bestimmen wir eine neue Matrix3D aus der letzten Rotationsachse und dem letzten Winkel. Während der nächsten Rotation wenden wir sowohl diese Startmatrix als auch die 3D-Rotation um die Achse in einer kombinierten Transformation auf unsere Box an. Dadurch verhindern wir, dass unsere Box jedesmal wenn wir eine neue Rotation einleiten in den ursprünglichen "unrotierten" Zustand zurückspringt.

function init(){ den virtuellen Trackball initiieren; Das Viewort-HTML-Element nach etwas Rotierbarem durchsuchen; Mousedown- oder Touchstart-Listener setzen, um die Rotation einzuleiten; Mouseup- oder Touchend-Listener setzen um die Rotation zu beenden; Mousemove- oder Touchmove-Listener für die Rotation setzen; Eine Matrix3d aus intialem Wikel und Rotationsachse bestimmen; } function startrotation(){ Click-Position ermitteln und auf den Trackball übersetzen; Den entsprechenden 3D-Vector in Variable "mouseDownVect" speichern; } function rotate(){ Maus-Position verfolgen und auf den Trackball übersetzen; Den entsprechenden 3d-Vector in Variable "mouseMoveVect" speichern; Die Rotationsachse ermitteln, indem die Normale auf mouseDownVect und mouseMoveVect bestimmt wird; Rotationswinkel zwischen mouseMoveVect und mouseDownVect bestimmen; Box mittels Startmatrix und rotate3d(axis, rotation-angle) transformieren; } function finishrotation(){ Neue Matrix3d aus dem letzen Winkel und Rotationsachse bestimmen; Die letzte Start-Matrix and die neue Matrix durch Bestimmen des Skalarprodukes zusammenfassen; Die zusammengefasste Matrix3d zur neuen Start-Matrix machen; }

Soweit ein Überblick der Logik. Wenn Ihr mehr herausfinden wollt, könnt Ihr Euch auch gerne direkt das Javascript anschauen, ich habe mein Bestes gegeben jeden Schritt zu kommentieren.

Das Script (ich habe es, nebenbei bemerkt, "traqball.js" getauft) steht unter MIT- und GPL-Lizenz. Ihr dürft es in Euren Projekten einsetzen solange Ihr die Lizenzbedingungen respektiert. Die Implementierung ist einfach:

Damit sind wir durch! Ich hoffe, diese kleine Lektion war für einige von Euch hilfreich. Wenn Ihr diese Methode bei einem Projekt anwenden konntet, freue ich mich natürlich über eine Mail mit einem Link.

Update; Ich habe traqball.js nach 2.0 geupdated. Source liegt nun auf Github. Mehr Infos hier..

Trackback

42 Kommentare zu „Natürliche Objektrotation mit CSS3 3D“

  1. Pingback by Tweets that mention Natural Object-Rotation with CSS3 3D | eleqtriq -- Topsy.com Fr. 5. Nov 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 Mo. 8. Nov 2010, 14:22

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

  3. Kommentar by Jerome Do. 11. Nov 2010, 14:19

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

  4. Kommentar by Dirk Do. 11. Nov 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ę Mo. 15. Nov 2010, 07:07

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

  6. Pingback by 10 New CSS3 Tutorials -December Mi. 1. Dez 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. Kommentar by Derek Di. 7. Dez 2010, 01:00

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

  8. Kommentar by Benton Do. 23. Dez 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. Kommentar by Dirk Fr. 24. Dez 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. Kommentar by Benton Fr. 24. Dez 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. Kommentar by Benton Fr. 24. Dez 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. Kommentar by Dirk Fr. 24. Dez 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. Kommentar by Paul Irish Fr. 11. Mär 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. Kommentar by Dirk Fr. 11. Mär 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. Kommentar by Mike Mo. 28. Mär 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. Kommentar by Cristy Mo. 28. Mär 2011, 23:49

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

  17. Kommentar by Sunny Singh Mo. 28. Mär 2011, 23:52

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

  18. Kommentar by Dirk Mi. 30. Mär 2011, 21:36

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

  19. Kommentar by Dirk Mi. 30. Mär 2011, 21:37

    Mike, Christy – which platforms are you using?

  20. Kommentar by jessicapretty Mo. 4. Apr 2011, 23:12

    This is your best topic yet!

    http://en.wikipedia.org/