sábado, 1 de diciembre de 2007

Dale aire al navegador exprimiendo el Javascript

Una parte muy importante de la Web actual es la visualización de animaciones. Ofrecen un añadido importante a cualquier interfaz de usuario siempre que se usen en su justa medida. En el ámbito Web podemos utilizar principalmente GIFs animados para el keyframing o generar animaciones de forma dinámica orientadas, en principio, a movimiento de objetos. Ésta segunda opción es muy interesante y flexible, aunque requiere codificar un pequeño Framework.

Las animaciones dinámicas requieren como mínimo de un Timer para ir notificando las actualizaciones de cada objeto. Javascript tiene este soporte aunque la resolución del Timer en la mayoría de los navegadores deja bastante que desear. De cualquier modo, si se utiliza una estrategia inteligente podemos aprovechar sus recursos de manera óptima.

Cuando se precisa animar algo siempre recurrimos al socorrido setInterval o setTimeout. Tendemos a crear uno por cada grupo de objetos que se van a mover al unísono. Esto no es beneficioso para el comportamiento del navegador principalmente porque se empieza a saturar la ejecución de hilos por cada Timer instanciado (ésto también depende del intérprete del navegador y su gestión interna de hilos).

A mí me gusta siempre extrapolar el diseño de algunas partes de una aplicación (sea Web o de escritorio, orientada a gráficos o incluso bases de datos) al campo de los videojuegos y en este caso tengo una buena piedra de toque. La idea es utilizar una clase manager que notifique a todos los objetos afectados del momento en que tienen que actualizar su estado interno. Este manager utilizará únicamente un Timer, con el ahorro de procesamiento para el navegador que eso conlleva. Si piensas que puede ser lento o no sincronice correctamente los objetos visuales afectados, permíteme que te diga que funciona como un reloj ;-)

La base del diseño lo forman simplemente una interfaz IAnimatable (debería darle una vuelta a este nombre :-D ) y una clase AnimatorManager. IAnimatable debe implementarla toda clase que necesite actualización en base a una frecuencia. Por su parte, AnimatorManager será la clase maestra encargada de manejar a su antojo a todos los implementadores de la interfaz, notificando cuando corresponda.


//Interfaz para todo componente susceptible de ser animado vía Timer
WebDock.IAnimatable = function() {}
WebDock.IAnimatable.Prototype = {
    onTick: function(){}
}
WebDock.IAnimatable.registerInterface('WebDock.IAnimatable');
 
//Manager de animación que controla el Timing de todos los implementadores de IAnimatable
WebDock.AnimatorManager = function(fps) {
    this._items = new Array();

    this._frameTime = 1000/fps;

    this._isprocessing=false;
    _oThis = this;
}

WebDock.AnimatorManager.prototype = {
    run: function() {

        this.timerID = setInterval(this.pump, this._frameTime);

    },
    
    add: function(animatable) {
        this._items.push(animatable);
    },
    
    remove: function(animatable) {
        for (var i=0;i<this._items.length;i++)
        {
            if (animatable==this._items[i])
            {
                this._items.splice(i, 1);

                i=this._items.length; //es más rápido que 'break'
            }
        }
    },
    
    //llama a la actualización de todos los componentes de animación registrados
    pump: function() {
        if (!_oThis._isprocessing) //establecemos contención para mantener sincronización correcta de cada frame
        {
            _oThis._isprocessing=true;
            for(var i=0;i<_oThis._items.length;i++)

            {
                var animatable=_oThis._items[i];
                animatable.onTick();
            }
            _oThis._isprocessing=false;
        }

    },
    dispose: function() {
    }
}
WebDock.AnimatorManager.registerClass('WebDock.AnimatorManager', null, Sys.IDisposable);


Por si a alguien le resulta raro el código Javascript anteriormente expuesto, simplemente apuntar que está desarrollado utilizando el Framework AJAX de ASP.NET. Éste permite herencia, interfaces, enumeraciones, eventos, etc. muy útiles a la hora de orientar a objetos en Javascript.