Angular.js: ng-show / hide está atrasado em um elemento que está animando

Criado em 16 jul. 2014  ·  17Comentários  ·  Fonte: angular/angular.js

Esse comportamento não é esperado se você estiver fazendo uma animação constante em algo como um botão giratório de carregamento e apenas quiser ocultá-lo ou exibi-lo imediatamente.

O elemento não foi informado para ser gerenciado pelo ng-animate, eu só quero que o ng-show / hide normal aconteça instantaneamente, e qualquer animação css que esteja em andamento deve ser irrelevante, eu acho.

Fiddle: http://jsfiddle.net/PvS8k/2/

Desmarque "show loader" e observe que leva alguns segundos para realmente desaparecer.

Acho que esta é uma mudança recente porque nosso carregador não estava grudando em nosso aplicativo antes da atualização. Acho que estávamos em 1.12 antes.

Devo estar faltando algo no meu entendimento de como usar / não usar o ng-animate, certo? Neste caso, eu realmente quero NÃO usá-lo, mas se eu puder usá-lo para ignorar esse elemento, isso também funcionaria. Comportamento definitivamente surpreendente.

ngAnimate broken expected use bug

Comentários muito úteis

@ tole42 Esta é a solução alternativa:

.YourElementClass.ng-animate { -webkit-animation: none 0s; }

Todos 17 comentários

O ngAnimate é ganancioso, pois assume que todos os quadros-chave / código de transição que estão ativos no elemento durante qualquer um dos gatilhos estão lá para serem animados por meio do ngAnimate. Portanto, neste caso, apesar de .loader não ter nada a ver com ngShow / ngHide, ainda é pego como uma animação válida.

Para contornar isso, simplesmente apague o estilo da animação quando a classe .ng-animate for aplicada ao elemento.

http://jsfiddle.net/PvS8k/4/

Este é um problema de herança CSS. Se houvesse uma maneira melhor de detectar estilos sem depender exclusivamente de getComputedStyle, esses tipos de peculiaridades poderiam ser evitados.

@matsko Acho que este caso especial é na verdade uma regressão. Como você pode ver no plunker de @SimpleAsCouldBe, ou neste plunker http://plnkr.co/edit/5cfIXfNryNOzK66q9YWL?p=preview , em 1.2.17, o elemento não desaparece imediatamente, mas em 1.2.16, ele faz. Provavelmente, isso se deve a https://github.com/angular/angular.js/commit/55b2f0e8620465559016b424967d90a86af597c0
Acho que isso justifica uma inspeção mais detalhada; pelo menos deve haver uma nota no changelog e na documentação sobre isso.

Obrigado pela solução alternativa, ele cumpre o esperado.

Dois pontos. Um é específico para ng-show e o outro é geral.

ng-show + animações infinitas:

Deveria uma animação infinita _realmente_ atrasar um esconderijo de ng-show ? Tentei contar os segundos e nem sempre são 4 segundos que ele espera para ocultá-lo. Às vezes, é 5 ou 7 ... parece variar. Além disso, se você ativar e desativar a caixa de seleção, ela nunca ficará oculta. Acho que há um bug real escondido aqui em algum lugar.

Filisófico

Como não contribuidor, não tenho muito o que dizer, mas talvez optar pela exclusão não seja a estratégia certa para esta biblioteca. É incrivelmente mágico ter uma biblioteca JS reagindo às regras CSS. Se eu tivesse que marcar elementos com ng-animatable , estaria muito mais preparado para isso.

@SimpleAsCouldBe sim, estive pensando o mesmo que talvez seja melhor exigir que todas as animações tenham um prefixo de classe animate- forma que ngAnimate então se conecte às animações.

Em relação ao atraso, não é que haja um atraso, é que o ngAnimate cancela a porção infinita e só executa a animação uma vez. Portanto, quando você o oculta, ele espera 2s * 1,5 = 3 segundos e, em seguida, executa a verificação de tempo limite e fecha a animação. Portanto, é uma janela de tempo desnecessária que não deveria existir - apenas a detecção de estilo CSS é altamente limitada.

@Narretz embora o commit que você forneceu possa afetar este exemplo em particular, ele na verdade não tem nada a ver com a regressão. A razão para isso é porque o estilo .ng-hide que existia antes da correção aplicou o display:none em um momento diferente. Mas a correção atual aplica-se SOMENTE depois que todas as animações são feitas (uma vez que a classe .ng-animate é removida).

Dê uma olhada na demonstração abaixo. Veja como existem dois conjuntos de seletores para a classe .blue : um com .ng-animate e sem ele. Apesar da classe .blue ser aplicada imediatamente, o ngAnimate ainda pensa que uma animação está para chegar e irá esperar por 3s (lembre-se de 2s * 1.5) antes de aplicar um tempo limite de fechamento. Normalmente seria 2s, mas um evento animationend nunca é disparado, pois a animação real no elemento (que está lá por causa de .loader ) é infinita e não é disparada por ng-class ( ou ng-show para o exemplo anterior). Assim sendo, isso não é afetado devido à correção de bug anterior.
http://jsfiddle.net/PvS8k/15/

Pode ser melhor tornar o ngAnimate menos ingênuo e apenas animar quando animated- está lá como um prefixo CSS em algum lugar.

Eu acho que se você estiver aberto a uma mudança significativa, como manipulação de animação opcional, criaria uma experiência de desenvolvedor menos surpreendente.

Você estaria aberto para prefixar com algo específico angular como .ng-animate vez de .animate normal?

Eu tenho o mesmo problema:
Com angular> = 1.2.17 http://jsfiddle.net/9krLr/17/
Com o angular 1.2.16 tudo está bem: http://jsfiddle.net/EZpQQ/1/

@ tole42 Esta é a solução alternativa:

.YourElementClass.ng-animate { -webkit-animation: none 0s; }

Obrigado pela ajuda. A solução alternativa não está funcionando :(
O que está errado?
http://jsfiddle.net/9krLr/21/

@ tole42 Nada nessa

Acho que vejo o mesmo problema que @ tole42 . Com o ngAnimate incluído em um módulo, a funcionalidade ng-hide / ng-show padrão não funciona corretamente para o caso base quando você _não_ está tentando animá-los. Em outras palavras, quando você usa ng-hide / ng-show em um elemento simples sem nenhuma classe de animação aplicada.

<div ng-hide="hideme">hide me</div>

Se eu quisesse uma animação dessa pele, faria algo como:

<div ng-hide="hideme" class="myanimation">hide me</div>

Existem algumas instâncias em um aplicativo em que você pode desejar animar uma exibição / ocultar e outras instâncias em que não deseja animar. Nos casos em que você não deseja animar, você não decora o elemento que está sendo mostrado / oculto com nenhuma classe de animação personalizada. Nesse caso, o Angular apenas adiciona a classe "ng-hide" a um elemento oculto.

Se você não incluir o ngAnimate em seu módulo, isso funcionará conforme o esperado: o elemento desaparece imediatamente. Mas, simplesmente incluindo ngAnimate, você obtém o comportamento mostrado nas duas charadas que

Algumas soluções alternativas que parecem funcionar são:

.ng-hide-add {
    transition: 0s linear all;
}

Ou:

.ng-hide {
    display: none !important;
}

Mas não parece correto que eles sejam necessários no caso em que você está simplesmente tentando esconder algo de uma maneira não animada.

Apenas para acompanhar algumas descobertas adicionais ...

Tentando reduzir o problema ao cenário mais simples possível - mostrando / ocultando divs simples - o problema não se manifesta.

Notei nos fiddles postados por @ tole42 que o Foundation.css estava sendo usado, e o exemplo mostra o problema em mostrar / ocultar campos de entrada. Minha experiência foi semelhante (ver o manifesto do problema com os campos de entrada), mas usando bootstrap.css. Explorando esses dois arquivos css mais de perto, os dois aplicam transições css aos campos de entrada.

Essas transições css parecem ser as verdadeiras culpadas, já que a animação angular está captando as durações delas. Isso é o que causa o atraso visual ao mostrar / ocultar esses elementos, mesmo se você não estiver tentando animar essa transição intencionalmente.

Portanto, não acho que seja necessariamente um problema que o Angular deva tentar resolver. É mais um efeito colateral do uso de animação angular com outras bibliotecas css, que podem ter algumas transições css incorporadas que você não esperava!

Demorou muito para rastrear a causa raiz, mas foi bom finalmente encontrar um motivo que fizesse sentido.

Sim, este é um problema importante com o ngAnimate, mas devido à falta de controle de getComputedStyle e a cascata de transições em outras bibliotecas, o ngAnimate não é capaz de detectar isso.

Existem duas soluções para corrigir isso:

1) Substitua o estilo da classe .ng-animate :
http://jsfiddle.net/9krLr/27/

A razão pela qual não funcionou para você antes foi a falta de especificidade do CSS.

2) Use $animateProvider.classNameFilter(regex) . A regex, neste caso, colocará um bloco rígido em todas as animações disparadas pelo ngAnimate e só permitirá que a animação ocorra se a regex corresponder a um className que está presente no elemento que você deseja animar.

Oh, colocar a configuração em um provedor é uma boa maneira de fazer, eu gosto muito disso, @matsko!

@pnutshellmenace obrigado pela solução rápida. Isso salvou meu dia.

Não entendo qual foi a solução final aqui. @matsko declarou:

Pode ser melhor tornar o ngAnimate menos ingênuo e apenas animado quando animado - existe como um prefixo CSS em algum lugar

Mas não vejo nenhuma implementação como essa. Dei uma olhada em $animateProvider.classNameFilter(regex) mas, novamente, fiquei com mais perguntas do que respostas. Qual é uma maneira prática de implementar isso e se houver mais de um nome de classe aplicável?

Não vejo razão para o ngAnimate assumir que eu quero animar tudo. Sempre haverá coisas que não quero animar e, atualmente, tenho mais coisas que não quero animar do que o contrário, o que torna desagradável estilizá-las manualmente para desativar as transições.

Edit: Encontrei esta entrada de blog que coloca as coisas em perspectiva ... Vou tentar: http://blog.fgribreau.com/2014/03/how-to-configure-ng-animate-to- work-on.html

Edição 2: adicionar $animateProvider.classNameFilter(/enable-animate/); funcionou, mas no processo encontrei alguns comportamentos interessantes:

Eu incluí um arquivo .js de um módulo angular de terceiros que dependia do ngAnimate e, de alguma forma, isso fazia com que as animações fossem disparadas até mesmo no meu próprio módulo. De alguma forma, o ngAnimate estava afetando o escopo do módulo pai?

Eu preferiria esta resposta usando $scope.$evalAsync(); para operação assíncrona. funciona bem.

Esta página foi útil?
0 / 5 - 0 avaliações