2011-08-06 13 views
5

Imagine que tengo una ventana con un ancho y alto definidos. Puedo clonar un elemento fuera de la vista en el lado derecho, y volar a través de la ventana en una posición aleatoria y con código como el siguiente:JQuery animar: ¿hay alguna forma de "detener" el activador completo?

... 
    .css({ 
     'left': BASE_NODE_INIT_LEFT_PX, 
     'top' : rand(BASE_NODE_INIT_TOP_MIN_PX, BASE_NODE_INIT_TOP_MAX_PX) + 'px', 
     'width': _width + 'px', 
     'height': _height + 'px', 
    }) 
    .animate({ 
     left: BASE_NODE_ANIMATE_DISTANCE_X_PX 
    } 
    ... 

suficiente animación simple. Aquí está la parte divertida: en cada paso me gustaría aplicar algo de física a este elemento.

Algo como esto:

 ... 
     step:function(currentStep){ 
      if($(this).data('animating-wind') !== true) 
      { 
       $(this).data('animating-wind',true); 
       var wind = (rand(1,2) == 2) ? '+=' + rand(1, 100) + 'px' : '-=' + rand(1, 100) + 'px'; 
       $(this).animate({ 
        'top': wind, 
        'text-indent': wind, 
       },{ 
        duration: 500, 
        complete: function(){ 
         $(this).data('animating-wind',false); 
        }, 
        queue: false 
       }); 
      } 
     } 
     ... 

Básicamente lo que ocurre es en lugar de volar en línea recta de derecha a izquierda, se ralentiza, acelera, y eleva al azar y salsas, así como yo estoy intentando.

El problema al que me enfrento es que a veces el "viento" es lo suficientemente fuerte como para que el elemento siga siendo visible en la ventana cuando el número de pasos ha alcanzado el total de pasos calculados, y simplemente desaparecerá (lo que sucede en mi complete:function(){ ...$(this).remove(); ...}

Obviamente, lo que está sucediendo es que JQuery cree que la animación está completa porque calculó los pasos y dijo "Estoy animando este objeto tantos píxeles durante tantos milisegundos en estos muchos pasos: está ahí". la animación de viento es fuera de la cola de animación por lo que este proceso de animación no es sabio.

Lo que me gustaría es comenzar la animación y seguir animando hasta que el elemento haya salido de la ventana gráfica en cualquier dirección. Una vez que ya no esté visible, ya sea desde su trayectoria de vuelo inicial o desde la pantalla de viento que sale de la pantalla, voy a detectarlo y activar la devolución de llamada onComplete. Lo único que se me ocurre es poner esta animación dentro de una función y en el complete:function(){} hacer una llamada recursiva a sí mismo, dentro del step:function(){} Haría la marca "visible en la ventana gráfica" y llamaría al si true. Esto simplemente parece un cheque muy caro: ejecutar ese cálculo más de 100 veces en 400-1200ms podría hacer que la animación sea entrecortada, así que estoy buscando una solución más elegante.

TL; DR: ¿Cómo mantengo la animación de algo con tal de que no ha llegado a su destino sin embargo, debido a las animaciones adicionales que se establecen en ella?

ACTUALIZACIÓN: apliqué @mu is too short's idea y se acercó con esto:

/** 
* Create a new clone of the master div for user interaction. Position it 
* randomly off the canvas, and animate it across at a random speed. 
*/ 

function spawn_target() { 
    spawn_timeout = setTimeout(function() { 
     var bonus = (rand(1, BONUS_ODDS) == BONUS_ODDS) ? ' ' + BONUS_CLASS : ''; 
     var _img_src = (bonus.length > 0) ? BONUS_NODE_IMG_SRC : BASE_NODE_IMG_SRC; 
     var _width = $('#' + BASE_NODE_ID).width(); 
      _width = Math.floor(rand(_width/3, _width)); 
     var _height = _width; 
     var _framerate = 10 - Math.floor(_height/10); 
     var _clone = $('#' + BASE_NODE_ID).clone().addClass(CLONE_CLASS + bonus).attr('id', CLONE_ID).appendTo('#' + CANVAS_ID).css({ 
      'left': BASE_NODE_INIT_LEFT_PX, 
      'top': rand(BASE_NODE_INIT_TOP_MIN_PX, BASE_NODE_INIT_TOP_MAX_PX) + 'px', 
      'width': _width + 'px', 
      'height': _height + 'px', 
     }) 

     $(_clone).children('img').attr('src', _img_src); 

     /** 
     * Handle the actual flight across the viewport 
     * @param object _this The clone we are animating manually 
     * @return object pointer This contains the interval so we can clear it later 
     */ 
     function fly(_this) { 

      var animating_wind = false; 
      var pointer = { 
       timer_id: null 
      }; 
      pointer.timer_id = setInterval(function() { 

       // Get rid of the node if it is no longer visible in the viewport 
       if (!$(_this).is(':onviewport')) { 
        clearInterval(pointer.timer_id); 
        $(_this).remove(); 
        adj_game_data(GAME_DATA_LIVES_ID, -1); 
        check_lives(); 
        spawn_target(); 
       } 

       // Move node along with slight realism 
       var distance = rand(FLY_DISTANCE_MIN, FLY_DISTANCE_MAX); 
       $(_this).css('left', '-=' + distance + 'px'); 

       // Only trigger the wind animation when it is idle 
       if (animating_wind !== true) { 
        animating_wind = true; 

        // Setup the wind physics 
        var _wind_power = rand(WIND_POWER_MIN, WIND_POWER_MAX); 
        var _wind_dir = (rand(1, 2) == 2) ? '+=' : '-='; 

        // Override wind direction to keep node inside viewport 
        if(($(_this).offset().top + $(_this).height() + _wind_power) > CANVAS_HEIGHT) 
        { 
         _wind_dir = '-='; 
        } 
        if(($(_this).offset().top - _wind_power) < CANVAS_TOP_BUFFER_PX) 
        { 
         _wind_dir = '+='; 
        } 

        var _wind = _wind_dir + _wind_power + 'px'; 

        // Apply the wind physics and don't let the node break the top or bottom 
        // boundaries of the viewport 
        $(_this).animate({ 
         'top': _wind 
        }, { 
         duration: WIND_ANIMATION_DURATION, 
         complete: function() { 
          animating_wind = false; 
         }, 
         queue: false 
        }); 
       } 
      }, _framerate); 

      return pointer; 
     } 
     $(_clone).data('interval', fly(_clone)); 

    }, rand(SPAWN_TARGET_TIMEOUT_MIN, SPAWN_TARGET_TIMEOUT_MAX)); 
} 

Esto es lo que tengo hasta ahora: http://jsfiddle.net/AlienWebguy/DmtQ7/embedded/result/

me gusta esta idea, aunque parece que jQuery debe tener algo incorporado en, que es lo que estaba esperando. Ahora, en lugar de $(obj).stop() tengo que clearInterval($(obj).data('interval').timer_id);. * Arañazos cabeza ...

+1

¿Has considerado realizar la animación a mano? Puede hacer un paso y llamar a 'setTimeout' para el próximo paso si todavía está en la página, luego continúe repitiéndolo hasta que se detenga. Entonces no tendrías que preocuparte por 'animate' tratando de predecir el número de pasos. –

+0

puede intentar usar + = en lugar de - = para que fluya "con el viento". Esto significaría que rebasaría la ubicación de la parada en lugar de subestimar, pero sin una demostración, no tengo idea de cómo resultaría este efecto. ¿Se puede publicar toda la función animada? –

+0

no puedo esperar para ver la demostración! –

Respuesta

1

Esto es lo que creo que debería estar haciendo (pseudo-JavaScript):

function animate_it($thing) { 
    var wind  = false; 
    var timer_id = setInterval(function() { 
     // Compute the movement for this step. 
     // Then factor in the wind effect and update the `wind` variable. 
     // Then apply the movement to `$thing`. 
     if(it_is_off_the_page($thing)) 
      clearInterval(timer_id); 
    }, n); 
} 

Si hay algo en el cálculo interno que se calcula una y otra vez, que calcular previamente en el nivel animate_it y deje que el cierre se cierre sobre él. Querrá jugar un poco para descubrir qué debería ser n.

Simplemente haz la animación a mano para que no estés peleando con la función animate de jQuery y detenga la animación cuando sepas que ya está hecha. Esto también te permite incluir una dirección y fuerza para el viento (para que tu objeto animado pueda ir hacia atrás) de forma bastante gratuita.

Es posible que desee obtener el timer_id de vuelta al mundo exterior para que pueda detener todo desde el exterior. En ese caso, tendría que desee simular un puntero con un objeto un elemento:

function animate_it($thing) { 
    var wind = false; 
    var pointer = { timer_id: null }; 
    pointer.timer_id = setInterval(function() { 
     // Compute the movement for this step. 
     // Then factor in the wind effect and update the `wind` variable. 
     // Then apply the movement to `$thing`. 
     if(it_is_off_the_page($thing)) { 
      clearInterval(pointer.timer_id); 
      pointer.timer_id = null; 
     } 
    }, n); 
    return pointer; 
} 

var timer = animate_it($thing); 

// And then later, to shut it down... 
if(timer.timer_id) 
    clearInterval(timer.timer_id); 

Usted probablemente querrá un "que se ha ido" de devolución de llamada en animate_it si has tenido una gran cantidad de objetos animados, que haría Permita que su lista externa de temporizadores esté limpia y sin errores.


El problema con tratar de hacer esto con animate es que animate calcula el número de pasos al principio y entonces usted puede cambiar eso. Necesita un número variable de pasos, pero animate no le importa (le importa incluso menos que un tejón miel).

+0

Ok esta es una idea sólida. Lo intentaré ahora y le dejaré saber cómo va. En cuanto al cálculo previo del viento, la idea del viento es crear una aleatoriedad. Básicamente estoy animando una mariposa de un lado a otro, y si piensas en cómo vuelan, no es como un pájaro. Se aceleran, reducen la velocidad, suben y bajan muy esporádicamente a pesar de sus mejores esfuerzos para seguir avanzando. Las "ráfagas" de viento cada medio segundo más o menos es lo que necesito calcular para crear esa aleatoriedad. Dame unos minutos y publicaré un violín. – AlienWebguy

+0

@AlienWebguy: Todo lo que estoy tratando de decir con el material "precomputado" es hacer lo menos posible en cada paso; por ejemplo, no haga '$ (x)' cuando pueda hacer 'var $ x = $ (x)' antes de que comience el temporizador. Y cerrar 'wind 'te permite tener un valor persistente sin la sobrecarga de llamar a' .data'. –

Cuestiones relacionadas