2009-10-20 13 views
19

W wielu przypadkach chciałbym, aby animacja była wykonywana synchronicznie. Zwłaszcza, gdy chcę zrobić serię sekwencyjnych animacji.Animacja synchroniczna JQuery

Czy istnieje prosty sposób na synchronizację wywołania funkcji jQuery animate?

Jedyny sposób, w jaki myślałem, to ustawienie flagi jako prawdziwej po zakończeniu animacji i oczekiwaniu na tę flagę.

Odpowiedz

24

jQuery nie może tworzyć synchronicznych animacji.

Pamiętaj, że JavaScript działa w wątku interfejsu użytkownika przeglądarki.

Jeśli utworzysz animację synchroniczną, przeglądarka zawiesi się do momentu zakończenia animacji.

Dlaczego musisz to zrobić?

Należy prawdopodobnie używać parametru wywołania zwrotnego jQuery i kontynuować swój kod metoda w zwrotnego, tak:

function doSomething() { 
    var thingy = whatever; 
    //Do things 
    $('something').animate({ width: 70 }, function() { 
     //jQuery will call this method after the animation finishes. 
     //You can continue your code here. 
     //You can even access variables from the outer function 
     thingy = thingy.fiddle; 
    }); 
} 

To się nazywa zamknięcie.

+0

myślę, że można uciec z wątku UI z setTimeout, tak, że z całą pewnością mogę używać nonblocking animację w funkcji setTimeout mieć sane kod szuka kolejnych animacji . –

+8

Mylisz się. 'setTimeout' nie wykonuje wywołania zwrotnego w innym wątku; oczekuje, że wątek interfejsu użytkownika stanie się wolny, a następnie wywoła wywołanie zwrotne w wątku interfejsu użytkownika. Dlatego programiści JavaScript nie muszą zajmować się wszystkimi zawiłościami rozwoju wątków. – SLaks

+0

przetestuj moje rozwiązanie odpowiedzi pod proszę – CuSS

1

Zgadzam się z @SLaks na ten temat. Powinieneś używać wywołań zwrotnych jQuery dla podanych animacji, aby utworzyć animację synchroniczną. Można w zasadzie przyjąć co masz do aktualnej animacji i podzielić go tak:

$yourClass = $('.yourClass'); 
$yourClass.animate({ 
    width: "70%" 
}, 'slow', null, function() { 
    $yourClass.animate({ 
     opacity: 0.4 
    }, 'slow', null, function() { 
     $yourClass.animate({ 
      borderWidth: "10px" 
     }); 
    }); 
}); 
+0

Prawdopodobnie wiesz, jak to będzie wyglądać, gdy skaluje się do 20 działań ... zobacz także moją odpowiedź na @SLaks. –

+0

Jeśli nie podoba ci się sposób, w jaki wygląda, spróbuj nie wciskać wywołań zwrotnych (lub tylko wciskać je trochę). To jedyny sposób, aby to zrobić. – SLaks

2

jQuery zapewnia „krok” wywołania zwrotnego dla jego .animate metoda(). można podłączyć do tego, aby zrobić synchronicznych animacje:

jQuery('#blat').animate({ 
    // CSS to change 
    height: '0px' 
}, 
{ 
    duration: 2000, 
    step: function _stepCallback(now,opts) { 
    // Stop browser rounding errors for bounding DOM values (width, height, margin, etc.) 
    now = opts.now = Math.round(now); 

    // Manipulate the width/height of other elements as 'blat' is animated 
    jQuery('#foo').css({height: now+'px'}); 
    jQuery('#bar').css({width: now+'px'}); 
    }, 
    complete: function _completeCallback() { 
    // Do some other animations when finished... 
    } 
} 
+2

Krokowe wywołanie zwrotne nie ma nic wspólnego z pytaniem. Kompletne wywołanie zwrotne jest dokładnie tym, co mówią inne odpowiedzi. – SLaks

+0

Próbuje wykonać jedną animację po zakończeniu. Nie próbuje animować dwóch żywiołów naraz. – SLaks

+0

Moje przeprosiny - mój mózg wstawił "a" tam, gdzie go nie było! – shuckster

6

Myślę, że należy spojrzeć na metody jQuery queue().

Dokumentacja kolejkowania nie tylko wyjaśnia, że ​​animacje jQuery tak naprawdę nie blokują interfejsu użytkownika, a właściwie kolejkuje je po sobie.

Zapewnia również sposób, aby uczynić swoje animacje i wywołania funkcji sekwencyjnej (to jest mój najlepszy zrozumienie tego, co masz na myśli przez „synchronicznego”), takich jak:

$("#myThrobber") 
    .show("slow")     // provide user feedback 
    .queue(myNotAnimatedMethod) // do some heavy duty processing 
    .hide("slow");    // provide user feedback (job's 

myNotAnimatedMethod() { // or animated, just whatever you want anyhow... 
    // do stuff 
    // ... 

    // tells #myThrobber's ("this") queue your method "returns", 
    // and the next method in the queue (the "hide" animation) can be processed 
    $(this).dequeue(); 

    // do more stuff here that needs not be sequentially done *before* hide() 
    // 
} 

To oczywiście przesada z przetwarzaniem asynchronicznym; ale jeśli twoja metoda jest właściwie zwykłą, starą synchroniczną metodą javascript, może to być sposób na zrobienie tego.

Nadzieja to pomaga, i przepraszam za mój słaby angielski ...

+0

synchroniczne oznacza 'fwrite (big_data)' zwraca PO zakończeniu 'fwrite'. Asynchroniczne oznacza, że ​​'fwrite' wróci natychmiast, a zapisanie dużych danych odbywa się równolegle/w innym czasie. –

+0

Następnie myślę, że to dobrze: jeśli chcesz, aby animacja $ ("# myThrobber") została wykonana synchronicznie po fwrite (big_data), możesz to zrobić za pomocą następujących dwóch instrukcji: (1) $ ("#myThrobber"). queue (fwrite (big_data)). animate (// cokolwiek); ORAZ (2) Zakończ metodę fwrite() za pomocą $ ("# myThrobber"). Dequeue(); UWAGA: moja próba była błędna podczas wywoływania $ (this) .dequeue(); powinno przeczytać: $ ("# myThrobber"). Dequeue(); Zapomniałem, że jak stworzyłem tę próbkę z kodu, gdzie potrzebowałem do .queue (jQuery.proxy (myNotAnimatedMethod, to)) Przepraszamy za to zamieszanie. –

0

natknąłem się na ten http://lab.gracecode.com/motion/ bardzo proste w obsłudze i działa świetnie w połączeniu z jQuery.

EDYCJA Linki wydają się być martwe. Jeśli poprawnie podążałem za tymi archiwami, kod jest pod https://github.com/feelinglucky/motion

+0

Szkoda, że ​​nie ma angielskiej dokumentacji. Dzięki i tak. –

+2

@Elazar: Możesz go przetłumaczyć; czyni to nieco zrozumiałym;) –

0

jQuery może tworzyć synchroniczne animacje.Sprawdź to:

function DoAnimations(){ 
    $(function(){ 
    $("#myDiv").stop().animate({ width: 70 }, 500); 
    $("#myDiv2").stop().animate({ width: 100 }, 500); 
    }); 
} 
+1

Z pisowni "funkcja" i "DoAnimations" źle ... –

+1

@ChrisFrancis Po prostu wprowadź poprawkę. – Mike

+1

To sekwencyjne, niezsynchronizowane – SLaks

0

Oto moduł, który połączyłem z powrotem, aby pomóc w uruchomieniu animacji sekwencyjnie.

Zastosowanie:

var seq = [ 
    { id: '#someelement', property:'opacity', initial: '0.0', value:'1.0', duration:500 }, 
    { id: '#somethingelse', property:'opacity', value:'1.0', duration: 500 } 
]; 

Sequencer.runSequence(seq); 

var Sequencer = (function($) { 
    var _api = {}, 
     _seq = {}, 
     _seqCount = 0, 
     _seqCallback = {}; 

    function doAnimation(count, step) { 
     var data = _seq[count][step], 
      props = {}; 

      props[data.property] = data.value 

     $(data.id).animate(props, data.duration, function() { 
      if (step+1 < _seq[count].length) { 
       doAnimation(count, ++step); 
      } else { 
       if (typeof _seqCallback[count] === "function") { 
        _seqCallback[count](); 
       } 
      } 
     }); 
    } 

    _api.buildSequence = function(id, property, initial, steps) { 
     var newSeq = [], 
      step = { 
       id: id, 
       property: property, 
       initial: initial 
      }; 

     $.each(steps, function(idx, s) { 
      step = {}; 
      if (idx == 0) { 
       step.initial = initial; 
      } 
      step.id = id; 
      step.property = property; 
      step.value = s.value; 
      step.duration = s.duration; 
      newSeq.push(step); 
     }); 

     return newSeq; 
    } 

    _api.initSequence = function (seq) { 
     $.each(seq, function(idx, s) {    
      if (s.initial !== undefined) { 
       var prop = {}; 
       prop[s.property] = s.initial; 
       $(s.id).css(prop); 
      }    
     }); 
    } 

    _api.initSequences = function() { 
     $.each(arguments, function(i, seq) { 
      _api.initSequence(seq); 
     }); 
    } 

    _api.runSequence = function (seq, callback) { 
     //if (typeof seq === "function") return; 
     _seq[_seqCount] = []; 
     _seqCallback[_seqCount] = callback; 

     $.each(seq, function(idx, s) { 

      _seq[_seqCount].push(s); 
      if (s.initial !== undefined) { 
       var prop = {}; 
       prop[s.property] = s.initial; 
       $(s.id).css(prop); 
      } 

     }); 


     doAnimation(_seqCount, 0); 
     _seqCount += 1; 
    } 

    _api.runSequences = function() { 
     var i = 0. 
      args = arguments, 
      runNext = function() { 
       if (i+1 < args.length) { 
        i++; 
        if (typeof args[i] === "function") { 
         args[i](); 
         runNext(); 
        } else { 
         _api.runSequence(args[i], function() { 
          runNext(); 
         }); 
        } 
       } 
      }; 

     // first we need to set the initial values of all sequences that specify them 
     $.each(arguments, function(idx, seq) { 
      if (typeof seq !== "function") { 
       $.each(seq, function(idx2, seq2) { 
        if (seq2.initial !== undefined) { 
         var prop = {}; 
         prop[seq2.property] = seq2.initial; 
         $(seq2.id).css(prop); 
        } 
       }); 
      } 

     }); 

     _api.runSequence(arguments[i], function(){ 
      runNext(); 
     }); 

    } 

    return _api; 
}(jQuery));