czwartek, 18 sierpnia 2011

Animation, czyli jak sprawnie animować elementy strony

Poprzednio pisałem na temat animacji. Tym razem rozbudowałem tę funkcję o dodatkowe przydatne możiwości.

var Animation = function(fn, params){
  params = params || {};
  params = params.implicit({
    frames: 10,
    fn: function(){},
    t: 30,
    transition: null,
    at: 0
  });
  var frame = Math.round(params.at * params.frames);
  var progress;
  
  var run = (function(){
    return params.transition
      ? function(p){
          progress = p;
          fn.call(res, params.transition(progress), params);
        }
      : function(p){
          progress = p;
          fn.call(res, progress, params);
        };
  })();
  var iid = setInterval(function(){
    run( (++frame)/params.frames );
    if( frame >= params.frames ){
      clearInterval(iid);
      params.fn.call(res, params);
    }
  }, params.t);
  run( frame/params.frames );
  
  var res = {
    stop: function(execEndFn){
      execEndFn = execEndFn || false;
      clearInterval(iid);
      if( execEndFn ){
        params.fn.call(res, params);
      }
      return this.progress();
    },
    progress: function(){
      return progress;
    },
    toString: function(){
      return '[Animation: '+ Math.round(progress * 1e2) +'%]';
    }
  };
  return res;
};

Przede wszystkim funkcja (klasa) nie zwraca już identyfikatora zwracanego przez funkcję setInterval. Zamiast tego zwraca obiekt z dwiema (nie licząc metody toString nadpisującej tę domyślną dla obiektu) metodami.
Pierwsza metoda stop umożliwia zatrzymanie animacji. Wywołanie jej z pierwszym parametrem równym true spowoduje wywołanie również funkcji, wywoływanej w chwili normalnego zakończenia animacji. Przez normalne zakończenie rozumiemy odtworzenie animacji do końca.
Kolejna metoda zwracanego obiektu pozwala pobrać aktualny stan animacji z zakresu <0; 1>.
Dodatkowo do opisanych już wcześniej parametrów dodałem dwa następujące.
Transition - pozwala przekazać funkcję przejścia. Dzięki temu możemy w prosty sposób zrealizować np. łagodne rozpoczęcie i/lub zakończenie animacji.
Kolejny nowy parametr at jest nie mniej ciekawy. Pozwala bowiem on na rozpoczęcie animacji od zadanego etapu. Animacja z niezerową wartością dla tego parametru będzie trwała już krócej czyli nie będzie rozciągnięta na zadaną ilość klatek (frames) a zostanie ucięta, co pozwala na niesamowicie proste zaimplementowanie animacji, która może zostać w dowolnej chwili przerwana i odtworzona wstecz do punktu wyjściowego.

Na koniec dodam, że "klasa" korzysta z funkcji implicit, w celu ustawienia domyślnych parametrów wywołania funkcji.

niedziela, 14 sierpnia 2011

Object update

Object.prototype.update = function(obj){
  for( var i in obj ){
    if( obj.hasOwnProperty(i) ){
      this[i] = obj[i];
    }
  }
  return this;
};

Funkcja update pozwala w prosty sposób rozbudować lub nadpisać dowolne właściwości obiektu.
Ponieważ funkcja operuje na referencji do obiektu, oraz zwraca tę referencję możemy z niej skorzystać na dwa sposoby.

Pierwszy z nich polega na utworzeniu obiektu i nadpisaniu/rozszerzeniu jego metod/wartości.
var obj = {
  a: 1,
  b: 2
};

obj.update({
  b: 3,
  c: 4
});

console.log(obj);

Drugi sposób to wywołanie metody update na naszym obiekcie w chwili jego utworzenia i zwrócenie na jego miejsce nowego obiektu.
var obj = {
  a: 1,
  b: 2
}.update({
  b: 3,
  c: 4
});

console.log(obj);

startInterval czyli natychmiastowy setInterval

var startInterval = function(fn, delay){
  var arg = [].slice.call(arguments, 2),
      that = this;  
  var newFn = function(){
    fn.apply(that, arg);
  };
  newFn();
  return setInterval(newFn, delay);
};

Powyżej zamieszczam funkcję startInterval. Działa dokładnie tak jak setInterval (z wyjątkiem możliwości przekazania pierwszego parametru jako String) z tą różnicą, że pierwsze wywołanie funkcji następuje natychmiastowo.
Funkcja jest wygodną alternatywą dla setInterval. Dzięki niej nie trzeba w kodzie tworzyć dodatkowych nazw dla funkcji, aby wywołać ją, oraz przekazać jako parametr do setInterval.