Gibt es eine Möglichkeit, über JavaScript den Winkel zu ermitteln, um den ein Element gedreht ist?
Scheint eine vernünftige Anfrage zu sein. Also haben wir etwas HTML
<div id="i-am-rotated">text</div>
Und es ist durch CSS gedreht
#i-am-rotated {
-webkit-transform: rotate(30deg);
-moz-transform: rotate(30deg);
-ms-transform: rotate(30deg);
-o-transform: rotate(30deg);
}
Unser Ziel ist es, die Zahl "30" aus diesem Element über JavaScript zu erhalten. Die moderne Methode zum Abrufen von Stilinformationen aus einem Element ist getComputedStyle() (Unterstützt in allen modernen Browsern und IE 9+, ältere IE-Versionen unterstützten currentStyle()). Versuchen wir, es mit getComputedStyle() zu bekommen.
var el = document.getElementById("i-am-rotated");
var st = window.getComputedStyle(el, null);
var tr = st.getPropertyValue("-webkit-transform") ||
st.getPropertyValue("-moz-transform") ||
st.getPropertyValue("-ms-transform") ||
st.getPropertyValue("-o-transform") ||
st.getPropertyValue("transform") ||
"Either no transform set, or browser doesn't do getComputedStyle";
Man könnte denken, der zurückgegebene Wert wäre "rotate(30deg)" und wir könnten parseInt() darauf anwenden und "30" erhalten. Aber leider funktioniert das nicht. Der tatsächliche Wert, den wir zurückbekommen, ist dieser
console.log(tr);
// matrix(0.8660254037844387, 0.49999999999999994, -0.49999999999999994, 0.8660254037844387, 0, 0)
Der Browser wandelt die CSS-Rotations-Transformation in eine Matrix-Transformation um. Ich stelle mir vor, er tut dies, um mehrere Transformationen auf dem einzelnen Element zu einem einzigen Wert zu vereinfachen. Was also tun wir?
Nicolas Gallager recherchierte die Matrix-Transformation für Rotations-Transformationen. Die im Wesentlichen dies ist
rotate(Xdeg) = matrix(cos(X), sin(X), -sin(X), cos(X), 0, 0);
Wir brauchen wirklich nur eine davon, um eine schnelle Gleichung zu erstellen. Wir müssen den Arkussinus (Umkehrung von Sinus, sin-1) der Werte ermitteln, wobei wir sicherstellen müssen, dass wir ihn in **Radiant** erhalten.
Zuerst legen wir die einzelnen Matrixwerte getrennt in die Hand
// UPDATE: below was causing errors sometimes...
// var values = tr.split('(')[1].split(')')[0].split(',');
// Replace with... (thanks Thierry)
var values = tr.split('(')[1],
values = values.split(')')[0],
values = values.split(',');
var a = values[0]; // 0.866025
var b = values[1]; // 0.5
var c = values[2]; // -0.5
var d = values[3]; // 0.866025
Dann wissen wir sin(X) == 0,5, also asin(0,5) == Radiant und Grad == Radiant * 180/π.
Also
var angle = Math.round(Math.asin(b) * (180/Math.PI));
console.log(angle);
// 30
Juhu!
Nicolas ging noch weiter, indem er auch die Skalierung berücksichtigte. Mit dem vollständigen Code unten kann der Rotationswert extrahiert werden, wenn beliebige andere Transformationen angewendet werden.
#complex-transform {
-webkit-transform: rotate(30deg) scale(1.2) skew(10deg) translate(5px, 5px);
-moz-transform: rotate(30deg) scale(1.2) skew(10deg) translate(5px, 5px);
-ms-transform: rotate(30deg) scale(1.2) skew(10deg) translate(5px, 5px);
-o-transform: rotate(30deg) scale(1.2) skew(10deg) translate(5px, 5px);
}
var el = document.getElementById("complex-transform");
var st = window.getComputedStyle(el, null);
var tr = st.getPropertyValue("-webkit-transform") ||
st.getPropertyValue("-moz-transform") ||
st.getPropertyValue("-ms-transform") ||
st.getPropertyValue("-o-transform") ||
st.getPropertyValue("transform") ||
"fail...";
// With rotate(30deg)...
// matrix(0.866025, 0.5, -0.5, 0.866025, 0px, 0px)
console.log('Matrix: ' + tr);
// rotation matrix - http://en.wikipedia.org/wiki/Rotation_matrix
var values = tr.split('(')[1];
values = values.split(')')[0];
values = values.split(',');
var a = values[0];
var b = values[1];
var c = values[2];
var d = values[3];
var scale = Math.sqrt(a*a + b*b);
// arc sin, convert from radians to degrees, round
// DO NOT USE: see update below
var sin = b/scale;
var angle = Math.round(Math.asin(sin) * (180/Math.PI));
// works!
console.log('Rotate: ' + angle + 'deg');
**UPDATE:** Es stellt sich heraus, dass diese Zeile zur Berechnung des Winkels viel zuverlässiger ist
var angle = Math.round(Math.atan2(b, a) * (180/Math.PI));
Vielen Dank an Nicolas, Divya und Oli für die Hilfe hinter den Kulissen.
So gut!
scale() und rotation() ok! aber...
Was ist mit skew() und translate()?
Die `skew()` (X oder Y) Transformation wirkt sich immer noch auf die 2x2-Matrix aus, die zur Darstellung der Rotation verwendet wird. Um Verzerrungen zu berücksichtigen, müssen Sie weitere Berechnungen durchführen. Christian Schaefer stieß in der Spezifikation auf Folgendes: http://www.w3.org/TR/2011/WD-css3-2d-transforms-20111215/#matrix-decomposition
Ich werde das wahrscheinlich nie benutzen, aber es war interessant zu sehen, dass der Computer eine Matrix zur Berechnung der Rotation verwendet und nicht Radiant oder Grad oder so etwas.
Leider reicht es nicht aus, von einem der Werte den Arkussinus zu bilden, da dies zu Mehrdeutigkeiten führt.
Ich überspringe den langweiligen Teil über die Umkehrung nicht-injektiver periodischer Funktionen (dieses Kommentarfeld ist zu klein); sehen Sie nur, dass
daher ist asin nicht ausreichend, um zwischen den beiden zu unterscheiden.
Sie benötigen auch Informationen vom Kosinus: Wenn er positiv ist, gibt es keine Sorge, wenn er negativ ist, müssen Sie 180° - arcsin(b) nehmen.
Speichern Sie dies unter "Ich werde das im echten Leben nie brauchen" ;)
Ich denke, es fehlt etwas bei den Berechnungen.
Ich habe das Fiddle modifiziert, um eine 130-Grad-Drehung anzuwenden (und dann noch weiter verschoben, damit man sie sehen kann), und das Skript meldete eine 50-Grad-Drehung.
http://jsfiddle.net/459jS/98/
Gute Beobachtung!
Sieht so aus, als könnten wir durch die Berücksichtigung der neuen Mathematik, auf die Nicolas oben verlinkt hat, die Arkussinus-Sache so ändern:
und es liefert gute Ergebnisse für alle getesteten Winkel, einschließlich des Problems, das Agos oben dargestellt hat.
Hey, wer das hier liest, könnte an Easy Transform interessiert sein. Es ist bei weitem nicht so leistungsfähig oder nützlich, wie es sein könnte. Forken Sie es und fügen Sie etwas hinzu, wenn Sie können, oder allgemeines Feedback und Nutzung wären toll.
Easy Transform @ Github
Ach, ich erinnere mich, ähnliche Dinge in Java3D gemacht zu haben – die Matrix zurück in einen Rotationswinkel umwandeln.
Ist das in Firefox und Opera dasselbe?
großartig
Mathematik macht so viel Spaß :)
Toller Beitrag, Chris, und auch tolle Analyse, Nicolas.
Wow, das muss ernsthaft eine jQuery-Methode werden.
Ich weiß nicht, wie lange ich auf so etwas gewartet habe, aber es war eine Weile.
Ich werde vielleicht eine Weile brauchen.
Vielen Dank
Toller Artikel! Danke für den Rat, sehr geschätzt!
Hallo, vielleicht bilde ich mir das nur ein, aber warum können wir nicht einfach verwenden
oder etwas Ähnliches, in meiner Chrome-Konsole gibt es zurück
Verpasse ich hier etwas?
Ich hatte Probleme mit diesem Code, als ich einen Rotationswert über 180 Grad ermitteln musste.
Es stellt sich heraus, dass die Funktion `atan2` nur einen Wert von -pi bis pi zurückgibt, sodass Sie nur Gradwerte von -180 bis 180 erhalten. Als ich zum Beispiel versuchte, eine Drehung von 270 Grad zu erhalten, gab der Code -90 zurück.
Die einzige Änderung besteht darin, zu prüfen, ob das Ergebnis in Radiant kleiner als Null ist. Wenn ja, addieren Sie `(2 * pi)` zum Ergebnis.
Sehen Sie sich meinen Pen hier für einen Vergleich an: http://codepen.io/jjeaton/pen/bzolH
Danke!! :D
Ich denke, die Beispiele sind leicht falsch (nicht für den Hauptartikelinhalt, sondern für die **scale**-Sachen) und es gibt auch ein bisschen zufälligen Code.
Dann funktionieren Matrizen, soweit ich das beurteilen kann (es ist sehr selten / mathematisch dokumentiert), wie folgt:
Dieser nächste Teil wird also wirklich verwirrend (das letzte Beispiel)
Ich würde es so schreiben:
Zunächst einmal hindert es uns daran, die Werte [0], [1], [2] usw. einfach den gleichermaßen nutzlosen a, b, c zuzuordnen, aber es zeigt uns vor allem, dass die Zeile
var scale = Math.sqrt(a*a + b*b);einfach entfernt werden kann, da wir die Skalierung in jeder Dimension bereits haben. Außerdem, wenn wir dieses Beispiel verwenden, dann scale = sqrt((0,866025 * 0,866025) + (0,5 * 0,5)) =0.9999996503
Tatsächlich wissen wir aber, dass die Matrix selbst besagt, dass die Skalierung auf der x-Achse 0,8660254037844387 und auf der y-Achse 0,8660254037844387 beträgt. Ich vermute also, diese Zahl sollte 0,8660254037844387 lauten. (Diese Gleichung findet die Hypotenuse eines Dreiecks mit falschen Punkten, es sei denn, ich bin dumm!)
Ich fange gerade an, diese Dinge zu lernen, also hoffe ich, dass ich richtig liege. Ich schäme mich sehr, wenn nicht. Fühlen Sie sich frei, diesen Kommentar zu löschen, wenn Sie ihn in den Artikel aufnehmen : )
Danke!! Ich habe mir Stunden des Kopfzerbrechens erspart!!
Vielen Dank! Das hat mir Stunden des Kopfzerbrechens erspart!