tovotu

18. April 2015
Lagesensoren und CSS-3D-Transformationen
Sprache:HTML5 - Libs:DeviceOrientationAPI

Einige frustrierte Firefox-OS-Nutzer stellten vor kurzem fest, dass keine auf dem Firefox Marketplace erhältliche Kompass-App tatsächlich nach Norden zeigte (Stand: April 2015). Zunächst schien es so, als sei dies allein darauf zurückzuführen, dass Chrome und Firefox in den jeweiligen Implementierungen der "Device Orientation API" einen unterschiedlichen Drehsinn annahmen.

Dann stieß ich aber auf eine App, die nicht nur die Nord-Süd-Ausrichtung, sondern alle möglichen Richtungen (Schwenken, Neigen und Rollen des Smartphones) der Lageveränderung in die Anzeige einbeziehen wollte: Cube Compass. Und die schien mir kompletten Unsinn anzuzeigen.

Also versuchte ich herauszufinden, wie man so einen Würfelkompass (nach Spezifikation) korrekt implementieren müsste. Irreführend war da zunächst ein einfaches Rezept vom MDN, Using device orientation with 3D transforms. Die App war nämlich schnell an diese Anleitung angepasst, zeigte aber immernoch Unsinn an.

Es schien kein Weg mehr an einem direkten Blick in die Spezifikationen vorbeizuführen. Die waren allerdings glücklicherweise absolut unmissverständlich.[1][2] Und so konnte ich nach einigem Ringen mit meiner räumlichen Vorstellungskraft eine funktionierende Implementierung des Würfelkompasses fertigstellen: tovotu.de/tests/compass/index.html Ein Blick in den (recht überschaubaren!) Quellcode kann schon sehr aufschlussreich sein.

Der Grund, warum es bisher noch keine funktionierende Implementierung dieses Würfelkompasses gab, sehe ich darin, dass viele irreführende, unvollständige und teilweise falsche Quellen zur Funktionsweise der "Device Orientation API" und zu CSS-3D-Transformationen existieren. Es folgt eine kleine Beschreibung der zugrundeliegenden Konzepte.

Für ein Smartphone gibt es drei unterschiedliche Koordinatensysteme, die im Zusammenhang mit unserem Thema eine Rolle spielen.

  • Zum einen gibt es das Geo-Koordinatensystem, das sich nach dem aktuellen Ort des Smartphones auf der Erde richtet. Die Geo-Z-Achse steht senkrecht auf der Erdoberfläche und zeigt in den Himmel. Die Geo-Y-Achse verläuft von Süden nach Norden und die Geo-X-Achse entsprechend von Westen nach Osten.
  • Zusätzlich schreibt die Ausrichtung des Smartphones ein Geräte-Koordinatensystem vor: Die Geräte-Z-Achse steht senkrecht auf dem Bildschirm und zeigt zum Betrachter. Die Geräte-Y-Achse verläuft vom unteren zum oberen Bildschirmrand und die Geräte-X-Achse verläuft vom linken zum rechten Bildschirmrand.
  • Das HTML-Koordinatensystem ist durch den Textsatz motiviert. Die HTML-X-Achse verläuft "von links nach rechts", die HTML-Y-Achse "von oben nach unten" (also wie in Leserichtung). Die HTML-Z-Achse stimmt mit der Geräte-Z-Achse überein. Hier ist zu beachten, dass dieses Koordinatensystem davon abhängt, ob der Nutzer das Gerät gerade im Porträt- oder Landschaftsmodus benutzt.

Über die "Device Orientation API" erhält man nun Informationen darüber, wie das aktuelle Geräte-Koordinatensystem aus dem Geo-Koordinatensystem hervorgeht. Und zwar wird der Übergang durch drei Drehungen (um unterschiedliche Drehachsen) realisiert: Wie das genau funktioniert, liest man am besten selbst in der Spezifikation (mit anschaulichen Abbildungen!) nach. Wichtig ist, dass die jeweiligen Drehachsen eben nicht die Achsen des Geo-Koordinatensystems sind, wie man vielleicht vermuten würde. Wie sich Münchhausen am eigenen Schopfe aus dem Sumpf zog, so werden hier als Drehachsen die Achsen des Geräte-Koordinatensystems während der Drehung selbst verwendet! Die in diese Drehungen involvierten Winkel alpha, beta und gamma stehen deshalb für Drehungen um (in der richtigen Reihenfolge!) gedrehte Drehachsen.

Zusätzlich ist der Drehsinn dieser Winkel alpha, beta und gamma entgegengesetzt zum Drehsinn der CSS-Transformations-Funktion rotate3d(). Das bedeutet, dass man den Würfel für die hier vorgestellte App erst mit Winkel -gamma (die HTML-Y-Achse ist ja entgegen der Geräte-Y-Achse orientiert) um die HTML-Y-Achse, dann mit Winkel beta um die mitgedrehte HTML-X-Achse und schließlich mit Winkel alpha um die inzwischen zweimal gedrehte HTML-Z-Achse drehen muss. Etwas vereinfacht sieht der Code dann so aus:

var b = e.beta,
    g = -e.gamma,
    a = e.alpha,
    axis_y = Array(0,1,0),
    axis_x = rotate(Array(1,0,0), axis_y, g),
    axis_z = rotate(rotate(Array(0,0,1), axis_y, g), axis_x, b);
 
document.getElementById("cube").style.transform =
    "rotate3d(" + axis_z[0] + ", " + axis_z[1] + ", " + axis_z[2] + ", " + a + "deg) " +
    "rotate3d(" + axis_x[0] + ", " + axis_x[1] + ", " + axis_x[2] + ", " + b + "deg) " +
    "rotate3d(" + axis_y[0] + ", " + axis_y[1] + ", " + axis_y[2] + ", " + g + "deg)";

Am Ende muss man noch beachten, dass sich das HTML-Koordinatensystem zusätzlich ändert, wenn der Benutzer die Bildschirmorientierung vom Portrait- in den Landschaftsmodus ändert. Dazu setzt man vor alle Drehoperationen eine weitere 90-Grad-Drehung um die HTML-Z-Achse.

  1. w3.org/TR/orientati...nt/#deviceorientation
  2. w3.org/TR/css3-tran...rms/#funcdef-rotate3d

Kommentare

Neue Kommentare zu diesem Artikel bitte per Mail an kommentare-520(at)tovotu.de!