Devolución de llamada cuando finaliza la transición CSS3

202

Me gustaría desvanecer un elemento (pasando su opacidad a 0) y luego, cuando termine, eliminar el elemento del DOM.

En jQuery esto es sencillo ya que puede especificar que "Eliminar" suceda después de que se complete una animación. Pero si deseo animar usando transiciones CSS3, ¿hay alguna forma de saber cuándo se ha completado la transición / animación?

CJ
fuente
3
debería ser similar a esto: stackoverflow.com/questions/6186454/…
Nicolas Modrzyk

Respuestas:

334

Para las transiciones, puede usar lo siguiente para detectar el final de una transición a través de jQuery:

$("#someSelector").bind("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function(){ ... });

Mozilla tiene una excelente referencia:

https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions#Detecting_the_start_and_completion_of_a_transition

Para las animaciones es muy similar:

$("#someSelector").bind("animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd", function(){ ... });

Tenga en cuenta que puede pasar todas las cadenas de eventos con prefijo del navegador al método bind () simultáneamente para admitir la activación del evento en todos los navegadores que lo admitan.

Actualizar:

Según el comentario dejado por Duck: utiliza el .one()método de jQuery para garantizar que el controlador solo se active una vez. Por ejemplo:

$("#someSelector").one("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function(){ ... });

$("#someSelector").one("animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd", function(){ ... });

Actualización 2:

El bind()método jQuery ha quedado en desuso y on()se prefiere el método a partir de jQuery 1.7.bind()

También puede usar el off()método en la función de devolución de llamada para asegurarse de que se disparará solo una vez. Aquí hay un ejemplo que es equivalente a usar el one()método:

$("#someSelector")
.on("animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd",
 function(e){
    // do something here
    $(this).off(e);
 });

Referencias

Jim Jeffers
fuente
13
Vale la pena señalar que la devolución de llamada se activará para cada elemento secundario que transite también. Es muy importante tener esto en cuenta en caso de que se pregunte por qué su devolución de llamada se dispara más veces de lo que esperaba. No estoy al tanto de ninguna solución a partir de ahora.
Lev
22
@Lev, podría mitigar esto fácilmente comparando el objetivo actual del evento con su objetivo. Entonces algo como: function (event) {if (event.target === event.currentTarget) {/ * Do stuff * /}}
Jim Jeffers
55
Sí, lo descubrí poco después de escribir el comentario. > _ <Gracias por publicarlo; ¡Estoy seguro de que ayudará a otros! :)
Lev
9
Usamos en .on()lugar de .bind()jQuery v1.7 + api.jquery.com/bind
olo
44
Todos los navegadores modernos ahora admiten el evento no prefijado. caniuse.com/#feat=css-transitions También tenga en cuenta que si tiene "transitionend webkitTransitionEnd", se activará dos veces en Chrome.
Michael S.
17

Otra opción sería usar jQuery Transit Framework para manejar sus transiciones CSS3. Las transiciones / efectos funcionan bien en dispositivos móviles y no tiene que agregar una sola línea de transiciones CSS3 desordenadas en su archivo CSS para realizar los efectos de animación.

Aquí hay un ejemplo que cambiará la opacidad de un elemento a 0 cuando haga clic en él y se eliminará una vez que se complete la transición:

$("#element").click( function () {
    $('#element').transition({ opacity: 0 }, function () { $(this).remove(); });
});

JS Fiddle Demo

ROFLwTIME
fuente
La devolución de llamada no funciona, tal vez se deba a un cambio en la API de tránsito, no lo sé, pero el ejemplo de violín no funciona. Activa el método hide antes de que se ejecute la animación (probado en Chrome)
Jonathan Liuti
@JonathanLiuti probado con FireFox 25, IE11, Chrome 31. Funciona bien.
ROFLwTIME
Sí @ROFLwTIME tienes toda la razón: parece que mi Chrome se estaba volviendo loco. Volvió a probar el violín hoy después de un reinicio limpio de Chrome y funciona como se esperaba. Culpa mía ! Perdón por esto.
Jonathan Liuti el
14

Hay un animationendevento que se puede observar, consulte la documentación aquí , también para las transitionanimaciones CSS puede usar el transitionendevento

No hay necesidad de bibliotecas adicionales, todas estas funcionan con vanilla JS

document.getElementById("myDIV").addEventListener("transitionend", myEndFunction);
function myEndFunction() {
	this.innerHTML = "transition event ended";
}
#myDIV {transition: top 2s; position: relative; top: 0;}
div {background: #ede;cursor: pointer;padding: 20px;}
<div id="myDIV" onclick="this.style.top = '55px';">Click me to start animation.</div>

Yehuda Schwartz
fuente
2
Esta es una respuesta límite de solo enlace . Debe expandir su respuesta para incluir tanta información aquí, y usar el enlace solo como referencia.
Goodbye StackExchange el
44
Votando este ya que es el primero que no depende de jQuery. No tiene sentido cortar un árbol entero por una rama.
Aaron Mason
7

Para cualquiera que pueda ser útil, aquí hay una función dependiente de jQuery con la que tuve éxito para aplicar una animación CSS a través de una clase CSS, y luego recibir una devolución de llamada después. Puede que no funcione perfectamente ya que lo estaba usando en una aplicación Backbone.js, pero puede ser útil.

var cssAnimate = function(cssClass, callback) {
    var self = this;

    // Checks if correct animation has ended
    var setAnimationListener = function() {
        self.one(
            "webkitAnimationEnd oanimationend msAnimationEnd animationend",
            function(e) {
                if(
                    e.originalEvent.animationName == cssClass &&
                    e.target === e.currentTarget
                ) {
                    callback();
                } else {
                    setAnimationListener();
                }
            }
        );
    }

    self.addClass(cssClass);
    setAnimationListener();
}

Lo usé un poco así

cssAnimate.call($("#something"), "fadeIn", function() {
    console.log("Animation is complete");
    // Remove animation class name?
});

Idea original de http://mikefowler.me/2013/11/18/page-transitions-in-backbone/

Y esto parece útil: http://api.jqueryui.com/addClass/


Actualizar

Después de luchar con el código anterior y otras opciones, sugeriría ser muy cauteloso con cualquier escucha para finalizar la animación CSS. Con múltiples animaciones en curso, esto puede volverse desordenado muy rápido para escuchar eventos. Sugeriría una biblioteca de animación como GSAP para cada animación, incluso las pequeñas.

David Sinclair
fuente
Gracias por compartirlo, lo he usado y editado agregando e.stopImmediatePropagation(); self.trigger(this.whichAnimationEvent()); //for purge existing event callback.apply(self);
BomAle
5

La respuesta aceptada actualmente se dispara dos veces para las animaciones en Chrome. Presumiblemente esto se debe a que reconoce webkitAnimationEndtan bien como animationEnd. Lo siguiente definitivamente solo se disparará una vez:

/* From Modernizr */
function whichTransitionEvent(){

    var el = document.createElement('fakeelement');
    var transitions = {
        'animation':'animationend',
        'OAnimation':'oAnimationEnd',
        'MSAnimation':'MSAnimationEnd',
        'WebkitAnimation':'webkitAnimationEnd'
    };

    for(var t in transitions){
        if( transitions.hasOwnProperty(t) && el.style[t] !== undefined ){
            return transitions[t];
        }
    }
}

$("#elementToListenTo")
    .on(whichTransitionEvent(),
        function(e){
            console.log('Transition complete!  This is the callback!');
            $(this).off(e);
        });
Chuck Le Butt
fuente
3
Sugeriría llamar a la función whichAnimationEvent () en su lugar, ya que trata con eventos de animación.
Jakob Løkke Madsen