Matthias Scharwies: Animation mit setTimeOut verzögert nicht

Guten Morgen!

Ich versuche seit geraumer Zeit Bewegung in meine canvas-Objekte zu bringen:
http://www.jsr-hersbruck.de/site/subdomains/canvas-gesicht.html

Leider verzögert ...

function animation_loop() {  
    Gesicht(ctx,xoff,0);  
    window.setTimeout(function() { i++; if (i < n) { animation_loop(); } }, 30000);  
};

... das window.setTimeout nicht wie beabsichtigt.

Ich kann leider trotz googeln das Problem nicht erkennen.

Sollte man lieber setInterval oder requestAnimationFrame verwenden?

Vielen Dank im Voraus!

Matthias

  1. Hallo Matthias,

    Leider verzögert ...

    function animation_loop() {

    Gesicht(ctx,xoff,0);
        window.setTimeout(function() { i++; if (i < n) { animation_loop(); } }, 30000);
    };

    
    >   
    > ... das window.setTimeout nicht wie beabsichtigt.  
      
    das ist auch kein Wunder, da du hier  
    ~~~javascript
      
    function init(){  
    	ctx.clearRect(0,0,520,110);  
    	while(xoff < 390) {  
    		//alert (xoff);  
    		animation_loop();  
    		// window.setTimeout("Gesicht()", 3000);  
    		xoff = xoff +1;  
    	}  
    }
    

    du Funktion animation_loop(); so schnell wie möglich 390 mal aufrufst. Du musst animation_loop(); nur einmal aufrufen und xoff = xoff +1; in animation_loop() ausführen.

    Ich kann leider trotz googeln das Problem nicht erkennen.

    Jetzt mal Hand aufs Herz: woher soll Google die Fehler in deinem Programm kennen?

    Gruß, Jürgen

    1. das ist auch kein Wunder, da du hier

      function init(){
      ctx.clearRect(0,0,520,110);
      while(xoff < 390) {
      //alert (xoff);
      animation_loop();
      // window.setTimeout("Gesicht()", 3000);
      xoff = xoff +1;
      }
      }

      
      >   
      > du Funktion animation\_loop(); so schnell wie möglich 390 mal aufrufst. Du musst animation\_loop(); nur einmal aufrufen und xoff = xoff +1; in animation\_loop() ausführen.  
        
      Vielen Dank, jetzt funktioniert's! :-)  
      
      >   
      > Jetzt mal Hand aufs Herz: woher soll Google die Fehler in deinem Programm kennen?  
        
      Das ist das große Problem als Anfänger - man kennt die Fragen zu den Antworten noch nicht, bzw. kann sie nicht genau formulieren.  
        
      
      > Gruß, Jürgen  
        
      Nochmals vielen Dank!  
      Matthias
      
  2. Lieber Matthias Scharwies,

    Animieren in JavaScript funktioniert in aller Regel so, dass man eine Funktion schreibt, die einen Schritt innerhalb der Bewegung ausführt. Diese Funktion wird dann in bestimmten Zeitabständen immer wieder ausgeführt.

    Wie Du die Steuerung umsetzt, die bestimmt, wann und wie oft diese Schritt-Funktion aufgerufen wird, ist relativ frei. Du kannst die Funktion selbst bestimmen lassen, wann sie damit aufhört sich selbst wieder aufzurufen:

    jetztIsSchluss = false; // globale Variable, entspricht window.jetztIsSchluss  
      
    // eigentliche Animationsfunktion (erhöht width-Eigenschaft eines Objektes)  
    function animationsSchritt () {  
        var o = document.getElementById("gesicht");  
        var wert;  
      
        if (o) {  
            // nächsten Schritt der Animation ausführen  
            wert = parseInt(o.style.width);  
            wert++;  
            o.style.width = String.concat(wert, "px");  
      
            if (wert > 100) {  
                jetztIsSchluss = true;  
            }  
      
            // weitermachen?  
            if (!jetztIsSchluss) {  
                window.setTimeout(animationsSchritt, 1000);  
            }  
        }  
    }  
      
    // Animation "zünden"  
    animationsSchritt();
    

    Du kannst auch vermeiden, mit globalen Variablen zu hantieren. Dazu packst Du alles in eine Funktion, damit die Variablen innerhalb dieser gelten, aber "nach außen" nicht sichtbar werden. Dann packst Du diese Funktion auch noch in Klammern, damit sie keinen Namen braucht, und führst sie sofort aus:

    (function () { ... }());

    Der Inhalt obiger Funktion sieht dann etwas anders aus:

    (function () {  
      
        var jetztIsSchluss = false; // lokale Variable  
      
        // eigentliche Animationsfunktion  
        var animationsSchritt = function () {  
            var o = document.getElementById("gesicht");  
            var wert;  
      
            if (o) {  
                // nächsten Schritt der Animation ausführen  
                wert = parseInt(o.style.width);  
                wert++;  
                o.style.width = String.concat(wert, "px");  
      
                if (wert > 100) {  
                    // jetztIsSchluss ist hier "sichtbar" (closure)!  
                    jetztIsSchluss = true;  
                }  
      
                // weitermachen?  
                if (!jetztIsSchluss) {  
                    window.setTimeout(  
                        /* Hier müssen wir eine neue (anonyme) Funktion notieren,  
                         * da die lokale Variable "animationsSchritt" im Kontext  
                         * des Timeout-Ereignisses nicht bekannt ist. Durch diese  
                         * Schreibweise ist nun aber "animationsSchritt" innerhalb  
                         * dieser anonymen Funktion bekannt ("closure"), sodass  
                         * diese "lokale Funktion" gefunden und ausgeführt werden  
                         * kann.  
                         */  
                        function () {  
                            animationsSchritt();  
                        },  
                        1000  
                    );  
                }  
            }  
        }  
      
        // Animation "zünden"  
        animationsSchritt();  
    }());
    

    Mit der obigen Schreibweise ist es nun wieder (fast) egal, wie Du Deine Funktionen nennst, da Du sie vor möglichen anderen JavaScripts auf Deiner Seite "versteckst".

    Liebe Grüße,

    Felix Riesterer.

    --
    ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
    1. @@Felix Riesterer:

      nuqneH

      Animieren in JavaScript funktioniert in aller Regel

      … nicht so gut. Es ruckelt. Deshalb sollte man das vermeiden und CSS verwenden.

      Sollte man lieber setInterval oder requestAnimationFrame verwenden?

      Auch dazu wird im Artikel was gesagt.

      Qapla'

      --
      „Talente finden Lösungen, Genies entdecken Probleme.“ (Hans Krailsheimer)
      1. Lieber Gunnar Bittersmann,

        wir werden uns wohl nie einig in der Frage "Animieren besser in CSS oder JavaScript?". Die Animationsmöglichkeiten mit CSS sind nicht universell. Und manchmal ist das Kombinieren diverser CSS-Animationen wesentlich ressourcenhungriger, als eine in JS passend geschriebene Lösung. Ich habe das im FF einmal vor zwei Jahren ausprobiert, kann also mit dieser Aussage mittlerweile sehr falsch liegen. Jedoch bist Du mir noch ein Beispiel schuldig, wie ein Browser-Spiel (ähnlich meines Mensch-ärgere-dich-nicht) mit CSS-Animationen anstelle von reinen JS-Animationen gelingen soll.

        Liebe Grüße,

        Felix Riesterer.

        --
        ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
        1. Lieber Gunnar Bittersmann,

          wir werden uns wohl nie einig in der Frage "Animieren besser in CSS oder JavaScript?".

          Lieber Gunnar,

          Ich versuche mich in meiner Freizeit an den Möglichkeiten von canvas und kann daher nicht auf css zurückgreifen.

          Lieber Felix,

          Danke für das Code-Beispiel. Ich werde es ausprobieren und versuchen anzuwenden.

          Liebe Grüße
          Matthias

          1. @@Matthias Scharwies:

            nuqneH

            Ich versuche mich in meiner Freizeit an den Möglichkeiten von canvas und kann daher nicht auf css zurückgreifen.

            Zu dem Wissen über der Möglichkeiten einer Technik sollte sich aber immer auch das Wissen um deren sinnvolle Anwendung gesellen.

            Qapla'

            --
            „Talente finden Lösungen, Genies entdecken Probleme.“ (Hans Krailsheimer)
        2. @@Felix Riesterer:

          nuqneH

          Jedoch bist Du mir noch ein Beispiel schuldig, wie ein Browser-Spiel (ähnlich meines Mensch-ärgere-dich-nicht) mit CSS-Animationen anstelle von reinen JS-Animationen gelingen soll.

          Mit Transitions. Mittels geeigneter transition-timing-function wie ease-in-ease-out ist es dann auch ein leichtes, die Spielsteine beschleunigt loslaufen zu lassen und dann abzubremsen.

          Zur besseren Hardwareunterstützung wird die Position der Spielsteine nicht mit left und top verändert, sondern mit transform: translation() | translationX() | translationY().

          Da die Spielsteine nicht den kürzesten Weg laufen sollen, sondern entlang der Spielfelder, wirst du auf das transitionend-Event lauschen wollen.

          Dass der Algorithmus immer noch ziemlich unsinnige Züge macht, ist dir bewusst?

          Qapla'

          --
          „Talente finden Lösungen, Genies entdecken Probleme.“ (Hans Krailsheimer)
          1. Lieber Gunnar Bittersmann,

            wie ein Browser-Spiel (ähnlich meines Mensch-ärgere-dich-nicht) mit CSS-Animationen anstelle von reinen JS-Animationen gelingen soll.

            Mit Transitions. Mittels geeigneter transition-timing-function wie ease-in-ease-out ist es dann auch ein leichtes, die Spielsteine beschleunigt loslaufen zu lassen und dann abzubremsen.

            ich überlege gerade, wie ein solcher Code aussehen muss, damit "es funzt". Ist der dann noch sinnvoll wartbar und ebenso deskriptiv, wie eine reine JS-Version? ich vermute nicht. Und da die Spiele-Logik in JS implementiert werden muss (mach das auch mal mit CSS!), sehe ich wirklich nicht ein, wieso Du in diesem Anwendungsfall noch immer darauf bestehst, dass eine mit CSS-Transitions realisierte Animation sinnvoller - sorry, mittlerweile formulierst Du im Grunde "performanter", was nicht wirklich dasselbe ist - wäre.

            Zur besseren Hardwareunterstützung wird die Position der Spielsteine nicht mit left und top verändert, sondern mit transform: translation() | translationX() | translationY().

            Aha, und daran kann ich dann ablesen, auf welchem Spielfeld sich der jeweilige Spielstein gerade befindet? Hmm... Es scheint, dass es Dir nicht gelingen will, mich von Deiner Sichtweise zu überzeugen.

            Da die Spielsteine nicht den kürzesten Weg laufen sollen, sondern entlang der Spielfelder, wirst du auf das transitionend-Event lauschen wollen.

            Auf ein Event lauschen? So richtig wie mit JavaScript? Warum nur sollte ich das wollen? Warum animiere ich nicht gleich völlig in JS??

            Dass der Algorithmus immer noch ziemlich unsinnige Züge macht, ist dir bewusst?

            Der Algorithmus sucht sich zufällig einen der möglichen Spielzüge aus und setzt ihn um. Was ist daran "unsinnig"? Vielleicht meinstest Du eigentlich "nicht zielorientiert"? Das wäre in der Tat eine wesentlich andere Aussage, denn unter "unsinnig" stelle ich mir vor, wie jemand mit dem Spielstein in den Vorratsbereich eines Mitspielers fährt, um dort eine wartende Spielfigur zu schlagen - oder die Figur gleich in die Nase zu stöpseln, oder ... hmm... wer kleine Kinder hat(te), kennt sicher noch andere Beispiele aus dem echten Leben für "unsinnig".

            Liebe Grüße,

            Felix Riesterer.

            --
            ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
            1. @@Felix Riesterer:

              nuqneH

              ich überlege gerade, wie ein solcher Code aussehen muss, damit "es funzt". Ist der dann noch sinnvoll wartbar und ebenso deskriptiv, wie eine reine JS-Version? ich vermute nicht.

              Ich vermute das Gegenteil. An der Logik ändert sich ja gar nichts. Wenn die Position mit translation() umgesetzt wird, ändert das auch sehr wenig am Code, s.u. Lediglich der Code für die Animation entfällt, was das Ganze noch kürzer macht.

              Und da die Spiele-Logik in JS implementiert werden muss (mach das auch mal mit CSS!)

              Es mag Freaks geben, die auch das machen würden. Das wäre aber Spielerei, sicherlich nicht sinnvoll. Außer um die Möglichkeiten auszuloten.

              So wie es der OP mit Canvas tut. Dabei kann man auch mal einen Smiley über den Bildschirm schieben. Wenn das aber das Ziel für eine produktive Anwendung ist, einen Smiley über den Bildschirm zu schieben, ist Canvas nicht das geeignete Mittel dafür.

              dass eine mit CSS-Transitions realisierte Animation sinnvoller - sorry, mittlerweile formulierst Du im Grunde "performanter", was nicht wirklich dasselbe ist - wäre.

              Für den Spieler ist sinnvoller und performanter dasselbe. Wirklich.

              Zur besseren Hardwareunterstützung wird die Position der Spielsteine nicht mit left und top verändert, sondern mit transform: translation() | translationX() | translationY().

              Aha, und daran kann ich dann ablesen, auf welchem Spielfeld sich der jeweilige Spielstein gerade befindet?

              Das kannst du an left und top genauso wenig. Genauso gut, wie deine Methode, die von Spielfeldkoordinaten in Bildschirmkoordinaten umrechnet (ich gehe mal ohne in den Code zu schauen davon aus, das es eine solche gibt), könnte sie statt der Werte von left und top auch die von translationX() und translationY() ändern.

              Hmm... Es scheint, dass es Dir nicht gelingen will, mich von Deiner Sichtweise zu überzeugen.

              Hmm… Liegt das an mir? ;-)

              Auf ein Event lauschen? So richtig wie mit JavaScript? Warum nur sollte ich das wollen?

              Um die Spielsteine ruckelfrei um die Ecke laufen zu lassen.

              Warum animiere ich nicht gleich völlig in JS??

              s.o.

              Der Algorithmus sucht sich zufällig einen der möglichen Spielzüge aus und setzt ihn um. Was ist daran "unsinnig"? Vielleicht meinstest Du eigentlich "nicht zielorientiert"?

              Verstehe, für dich ist der Sinn des Spiels etwas anderes als das Ziel des Spiels. Ziel ist es, zu gewinnen. Sinn ist, sich die Zeit zu vertreiben. ;-)

              Das wäre in der Tat eine wesentlich andere Aussage, denn unter "unsinnig" stelle ich mir vor, wie jemand mit dem Spielstein in den Vorratsbereich eines Mitspielers fährt, um dort eine wartende Spielfigur zu schlagen - oder die Figur gleich in die Nase zu stöpseln, oder ... hmm... wer kleine Kinder hat(te), kennt sicher noch andere Beispiele aus dem echten Leben für "unsinnig".

              Nach dieser Definition lässt dein Spiel kein unsinnigen Züge zu.

              Ich würde jene eher als ungültig bezeichnen. Und als unsinnig die, die dem angestrebten Gewinn des Spiels zuwider sind.

              Bspw. nicht ins Haus zu gehen, wenn man davor steht, sondern einen anderen Spielstein zu setzen. Oder sich aufs Loch (Einsatzfeld) eines anderen zustellen, obwohl man andere Steine im Spiel hat. Oder das Loch eines anderen nicht schnellstmöglich wieder zu verlassen …

              Qapla'

              --
              „Talente finden Lösungen, Genies entdecken Probleme.“ (Hans Krailsheimer)