php-neuling: JavaScript + GD - Animation, Bild rotieren

hallo,

wie drehe/rotiere ich ein img-element mit js/php?

habe bisher ohne erfolg nach einer guten möglichkeit gesucht, ein bild zu drehen.
das neue canvas-element ist toll, allerdings machen die alten IEs da ein strich durch die rechnung.
man kann natürlich für jeden drehungsschritt ein eigenes bild mit photoshop, gimp o.ä. erzeugen und dann per js die src ändern. aber vorgestern bin ich über die imagerotate-funktion der gd-lib gestoßen und habe versucht, es damit hinzukriegen (siehe code unten).

vorweg:

  • habe zwecks überschaubarkeit auf eine verallgemeinerung meiner funktion rotiere() mittels parameter-übergabe verzichtet
  • die funktion ist zum drehen quadratischer bilder gedacht, kann aber natürlich mit etwas gerechne modifiziert (u. verallgemeinert) werden
  • das bild, das ich drehe, hat die maße 50x50 px;
  • ID(id) steht für: document.getElementById('id') - IDy(id) steht für: document.getElementById('id').style
  • bitte nur konstruktive kritik - bin ein noob in den bereichen html, js, php. js kenne ich seit wenigen monaten, php seit 2 wochen und die gd-lib seit vorgestern; mir ist also klar, dass mein script stümperhaft ist ;}
  • werde bei gelegenheit eine zeichnung zur veranschaulichung hinzufügen - falls interesse da ist und meine funktion nicht als schrott erkannt wird ^^
  
<html>  
<head>  
<title>rotate image with js and gd</title>  
<style>  
	input		{position:absolute; left:50px;}  
	#bild		{position:absolute; left:200px; top:200px;}  
</style type="text/css">  
<script language="JavaScript" type="text/javascript" src="Muster-Scripte/IDy.js"></script>  
<script type="text/javascript">  
  
// fange Rotation bei 0° an  
                var deg = 0;  
	  
	function rotiere() {  
// eine 360°-Rotation, danach wird deg wieder auf 0 gesetzt  
		if (deg < 360)  
		{  
// die 360°-Rotation hier im Bsp. in 5°-Schritten alle 5ms (siehe  
// setTimeout unten)  
			deg += 5;  
// die GD-Funktion imagerotate() passt die Größe des Bildes der Drehung an,  
// wobei 2 Ecken des Ursprungsbildes jeweils die obere und die linke Kante  
// des neuen Bildes berühren und 4 mit der im 3. Parameter von imagerotate()  
// definierten Farbe gefüllten Dreiecke die durch diese  
// Größenanpassung entstandenen "Leerräume" bilden.  
// Die neue Größe des Bildes ist nach den Winkelsätzen  
// [cos(a)=Ankathete/Hypotenuse und sin(a)=Gegenkathete/Hypotenuse]  
// also Ankathete + Gegenkathete eines der 4 gleichen Dreiecke,  
// wobei die Hypotenuse der Breite/Höhe des Ursprungsbildes entspricht -  
// hier: 50px!  
// Zur Bestimmung der neuen Größe benötigen wir also den Winkel (hier:  
// moddeg), der genau der Winkel deg Modulo 90 ist. Ausnahme:  
// Ist deg genau 90°, 180°, 270° oder 360°, so wäre deg % 90 = 0;  
// also setzen wir für diese Fälle moddeg = 90.  
			if (deg != 90 && deg != 180 && deg != 270 && deg != 360)  
			{  
				var moddeg = deg % 90;  
			} else {  
				var moddeg = 90;  
			}  
			var ankat = 50 * Math.cos(moddeg * Math.PI / 180);  
			var gegkat = 50 * Math.sin(moddeg * Math.PI / 180);  
// Da die Breite/Höhe eines Bildes eine ganzzahlige Pixelgröße ist, kann  
// die neue Breite/Höhe (bis auf die Ausnahmefälle) nicht genau  
// Ankathete + Gegenkathete sein -> abrunden und um 1 erhöhen!  
			if (deg != 90 && deg != 180 && deg != 270 && deg != 360)  
			{  
				var newWidth = Math.floor(ankat + gegkat) + 1;  
			} else {  
				var newWidth = ankat + gegkat;  
			}  
// Jetzt zum neuen Rotationszentrum. Das Rotationszentrum bei imagerotate()  
// liegt in der Mitte des Bildes. Da allerdings die Bildgröße in  
// positiver x- und y-Richtung angepasst wird und das "alte Bild"  
// also im "neuen Bild" die obere und die rechte Kante berührt, bekommt  
// man ohne Modifikation nicht einmal eine Drehung des Bildes um dessen  
// ursprüngliches Rotationszentrum. Hier nun eine Modifikation, die das  
// Verschieben des Rotationspunktes auf die Mitte der Unterkante des  
// Ursprungsbildes bewirkt, wobei berücksichtigt wird, dass die  
// Unterkante mit den Drehungen variiert; es wird hier simuliert, es  
// gäbe lediglich das Ursprungsbild und nicht die 4 Dreiecke, die durch die  
// Bildvergrößerungen entstehen.  
// Mit einer Prise Mathe ermittelt man nun die Mitte der Unterkante des  
// gedrehten Ursprungsbildes und verschiebt das neue Bild in  
// x- und y-Richtung, s.d. diese ermittelte Mitte mit der des unge-  
// drehten Bildes - also mit dem Rotationszentrum - übereinstimmt.  
			if (deg <= 90)  
			{  
				var newLeft = Math.round(-newWidth + ankat/2 + 225);  
				var newTop = Math.round(-newWidth + gegkat/2 + 250);  
			}  
			if ((deg > 90) && (deg <= 180))  
			{  
				var newLeft = Math.round(-newWidth + gegkat/2 + 225);  
				var newTop = Math.round(-ankat/2 + 250);  
			}  
			if ((deg > 180) && (deg <= 270))  
			{  
				var newLeft = Math.round(-ankat/2 + 225);  
				var newTop = Math.round(-gegkat/2 + 250);  
			}  
			if ((deg > 270) && (deg <= 360))  
			{  
				var newLeft = Math.round(-gegkat/2 + 225);  
				var newTop = Math.round(-newWidth + ankat/2 + 250);  
			}  
// Dies ist die Drehung selbst: Die Bildquelle ist die Datei drehstern.php,  
// in der das Bild mit GD erstellt wird.  
//Der src wird eine GET-Variable angehängt (grad), die in drehstern.php mit  
// $_REQUEST["grad"] abgerufen und in die Variable $grad gespeichert wird.  
			bildSrc = 'drehstern.php?grad='+deg;  
			ID('bild').src = bildSrc;  
// Die Position des neuen - gedrehten - Bildes zur Beibehaltung des  
// Rotationszentrums korrigieren.  
			IDy('bild').left = newLeft + 'px';  
			IDy('bild').top = newTop + 'px';  
// 5° done. 355 more to go:  
			setTimeout('rotiere(5)',5);  
		}  
		else {deg = 0;}  
	}  
  
  
</script>  
</head>  
<body>  
	<h1>GD-image einbinden</h1>  
	<input type="button" value="Rotiere den Stern" onclick="javascript:rotiere()" />  
	<div>  
		<img src="drehstern.php" id="bild" />  
	</div>  
</body>  
</html>  

drehstern.php

  
<?php  
  
header('Content-type: image/png');  
  
$pic = 'bilder/stern.jpg';  
$grad = $_REQUEST["grad"];  
  
$quelle = imagecreatefromjpeg($pic);  
$bgfarbe = imagecolorallocate($quelle, 255, 255, 255);  
$rotate = imagerotate($quelle, $grad, $bgfarbe);  
  
imagepng($rotate);  
imagedestroy($quelle);  
imagedestroy($rotate);  
  
?>  

die rotation ist beim ersten drücken des buttons etwas ruckelig, bei allen folge-drücken dann aber i.o.
kann mir jemand erklären, woran das liegt?

freue mich über jede konstruktive kritik und auch über anregungen/hinweise/links dazu, wie man eleganter bilder drehen könnte (ohne bibliotheken).

vielen dank,

ein neuling

  1. wie drehe/rotiere ich ein img-element mit js/php?

    Wenn du nur "ganze" Winkel brauchst 90, 180, 270 - kannst du auch mit CSS (transform + rotate) und für den Internet Explorer mit dem Transform-Filter (BasicImage, rotate) arbeiten.

    Damit reduzierst du deinen Code auf der serverseite auf 0 und auf der JavaScript-Seite auf etwa 5 Zeilen sowie auf der CSS-Seite auf etwa 20 Zeilen.

    1. Grüße,

      Wenn du nur "ganze" Winkel brauchst

      warum das? CSS3 erlaubt doch beliebige werte 0-360°, zudem kann man das ganze mit transition-duration animieren....
      MFG
      bleicher

      --
      __________________________-

      FirefoxMyth
      1. Wenn du nur "ganze" Winkel brauchst
        warum das? CSS3 erlaubt doch beliebige werte 0-360°,

        Beim IE-Filter nur 90°-Schritte.

        zudem kann man das ganze mit transition-duration animieren....

        Kein IE-Support.

        1. ja, habe mich da jetzt mal etwas reingelesen - danke für den tipp nochmal.
          bei FF kann man mit javascript wunderbar über style.MozTransform eine
          drehung animieren und zudem mit -moz-origin das rotationszentrum ändern :-)
          leider scheint mir eine dynamische manipulation der IE-filter mit javascript
          etwas komplizierter ^^

          1. ja, habe mich da jetzt mal etwas reingelesen - danke für den tipp nochmal.
            bei FF kann man mit javascript wunderbar über style.MozTransform eine
            drehung animieren und zudem mit -moz-origin das rotationszentrum ändern :-)
            leider scheint mir eine dynamische manipulation der IE-filter mit javascript
            etwas komplizierter ^^

            Musst du auch nicht - definiere eifach eine Reihe an Klassen für die Rotationszustände und wechsle diese per JavaScript.

            1. Musst du auch nicht - definiere eifach eine Reihe an Klassen für die Rotationszustände und wechsle diese per JavaScript.

              ok, aber anscheinend gibt's für die rotation mit ie-filter dasselbe problem
              wie bei der gd-funktion imagerotate(): man muss der translation des
              rotationszentrums einige mathematische aufmerksamkeit widmen.
              wenn ich das richtig sehe, wird der code bspw. bei einer drehung um 360°
              in - sagen wir - 5°-schritten um ein beliebiges rotationszentrum keineswegs
              erheblich kürzer mit ie-filter statt gd.
              bleibt natürlich der vorteil, dass nicht jeder rotations-zwischenschritt
              neu als "gd-bild" hochgeladen werden muss.
              sowas wie -moz-origin gibt's nicht für ie, oder?
              für die einfache rotation ohne origin-verschiebung ist für ie
              .filters.item("DXImageTransform.Microsoft.Matrix").M11 = Math.cos(deg*Math.PI/180);....M12 =...M22 = Math.cos(deg*Math.PI/180),
              was für ff .style.MozTransform = "rotate("+deg+"deg)"; ist.
              gruß

              1. Hi,

                ok, aber anscheinend gibt's für die rotation mit ie-filter dasselbe problem
                wie bei der gd-funktion imagerotate(): man muss der translation des
                rotationszentrums einige mathematische aufmerksamkeit widmen.

                Ein bisschen, ja: Correcting Transform Origin and Translate in IE

                MfG ChrisB

                --
                RGB is totally confusing - I mean, at least #C0FFEE should be brown, right?
                1. hey chris,
                  danke für den link!
                  hab mich schon mit eigenen mathe-anstrengungen um das problem gekümmert
                  mittlerweile - eigentlich nicht so schwer.
                  könnt alles soviel einfacher sein, wenn man IE ignorieren könnte lol
                  aber wenn man sich da erstmal reingefuchst hat, dann macht's auch
                  irgendwie spaß, diese matrizen-schlacht ^^
                  liebe grüße

    2. danke dir!
      sehr merkwürdig, dass ich über diese transform- bzw. -moz-transform-eigenschaften zuvor noch nicht gestolpert bin |~D
      da werd ich gleich mal mit rumbasteln...
      lieben gruß

  2. die rotation ist beim ersten drücken des buttons etwas ruckelig, bei allen folge-drücken dann aber i.o.
    kann mir jemand erklären, woran das liegt?

    Nachtrag: vergessen deine Frage zubeantworten.

    Dass es ruckelt liegt daran, dass die Bilder einerseits von PHP erzeugt werden müssen und andererseits vom Browser natürlich erst geladen werden müssen ;) das kostet.