Retorno de chamada quando a transição CSS3 termina

202

Eu gostaria de desvanecer um elemento (fazendo a transição de sua opacidade para 0) e, quando terminar, remova o elemento do DOM.

No jQuery, isso é simples, pois você pode especificar que "Remover" ocorra após a conclusão da animação. Mas se eu desejar animar usando transições CSS3, existe alguma maneira de saber quando a transição / animação foi concluída?

CJ
fonte
3
deve ser semelhante a este: stackoverflow.com/questions/6186454/…
Nicolas Modrzyk

Respostas:

334

Para transições, você pode usar o seguinte para detectar o final de uma transição via jQuery:

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

Mozilla tem uma excelente referência:

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

Para animações é muito semelhante:

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

Observe que você pode passar todas as seqüências de eventos com prefixo do navegador para o método bind () simultaneamente para dar suporte ao disparo de eventos em todos os navegadores que o suportam.

Atualizar:

De acordo com o comentário deixado por Duck: você usa o .one()método jQuery para garantir que o manipulador seja acionado apenas uma vez. Por exemplo:

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

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

Atualização 2:

O bind()método jQuery foi descontinuado e o on()método é preferido a partir de jQuery 1.7.bind()

Você também pode usar o off()método na função de retorno de chamada para garantir que seja acionado apenas uma vez. Aqui está um exemplo que é equivalente a usar o one()método:

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

Referências:

Jim Jeffers
fonte
13
Vale a pena notar que o retorno de chamada será acionado para cada elemento filho que também transitar. É muito importante ter em mente se você está se perguntando por que o retorno de chamada é acionado mais vezes do que o esperado. Não tenho conhecimento de nenhuma solução alternativa a partir de agora.
Lev
22
@ Lev, você pode facilmente mitigar isso comparando o currentTarget do evento com seu destino. Então, algo como: function (event) {if (event.target === event.currentTarget) {/ * fazer coisas * /}}
Jim Jeffers
5
Sim, eu descobri isso logo depois que escrevi o comentário. > _ <Obrigado por postar; Tenho certeza que vai ajudar os outros! :)
Lev
9
Nós usamos .on()em vez de .bind()para jQuery v1.7 + api.jquery.com/bind
olo
4
Todos os navegadores modernos agora suportam o evento não corrigido. caniuse.com/#feat=css-transitions Observe também que, se você tiver "transiçãoend webkitTransitionEnd", ele será acionado duas vezes no Chrome.
Michael S.
17

Outra opção seria usar o jQuery Transit Framework para lidar com suas transições CSS3. As transições / efeitos têm bom desempenho em dispositivos móveis e você não precisa adicionar uma única linha de transições CSS3 confusas no seu arquivo CSS para fazer os efeitos de animação.

Aqui está um exemplo que fará a transição da opacidade de um elemento para 0 quando você clicar nele e será removido assim que a transição estiver concluída:

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

JS Fiddle Demo

ROFLwTIME
fonte
O retorno de chamada não está funcionando - talvez seja devido a uma alteração na API do transporte público que não conheço, mas o exemplo do violino não está funcionando. Ele aciona a pele método antes de a animação é executada (tentou em cromo)
Jonathan Liuti
@JonathanLiuti testado usando o FireFox 25, IE11, Chrome 31. Funciona bem.
ROFLwTIME
Sim @ROFLwTIME você está totalmente certo - parece que meu chrome estava ficando louco. Teste novamente o violino hoje após uma reinicialização limpa do chrome e está funcionando conforme o esperado. Foi mal ! Desculpe por este.
Jonathan Liuti
14

Há um animationendevento que pode ser observado, veja a documentação aqui , também para transitionanimações em css , você pode usar o transitionendevento

Não há necessidade de bibliotecas adicionais, todas elas funcionam com 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
fonte
2
Esta é uma resposta somente para o limite do link . Você deve expandir sua resposta para incluir o máximo de informações aqui e usar o link apenas para referência.
Adeus StackExchange
4
Votar esta como a primeira que não depende do jQuery. Não faz sentido cortar uma árvore inteira para um galho.
Aaron Mason
7

Para qualquer um que possa ser útil, aqui está uma função dependente de jQuery, com a qual tive sucesso ao aplicar uma animação CSS por meio de uma classe CSS e depois receber um retorno de chamada posteriormente. Pode não funcionar perfeitamente desde que eu o usei em um aplicativo Backbone.js, mas talvez seja ú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();
}

Eu usei isso assim

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

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

E isso parece útil: http://api.jqueryui.com/addClass/


Atualizar

Depois de lutar com o código acima e outras opções, eu sugeriria ser muito cauteloso com qualquer escuta da animação CSS. Com várias animações em andamento, isso pode ficar confuso muito rápido para a escuta do evento. Eu sugeriria fortemente uma biblioteca de animação como o GSAP para todas as animações, mesmo as pequenas.

David Sinclair
fonte
Obrigado por compartilhar, eu tê-lo usado e editado adicionando e.stopImmediatePropagation(); self.trigger(this.whichAnimationEvent()); //for purge existing event callback.apply(self);
BomAle
5

A resposta aceita atualmente é acionada duas vezes para animações no Chrome. Presumivelmente, isso ocorre porque reconhece webkitAnimationEnde também animationEnd. Definitivamente, o seguinte será acionado apenas uma 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
fonte
3
Eu sugeriria chamar a função whichAnimationEvent (), pois trata de eventos de animação.
Jakob Løkke Madsen