web-dev-qa-db-fra.com

Déclenchement de la transition CSS sur l'élément ajouté

Comme cette question observe, les transitions CSS immédiates sur les éléments récemment ajoutés sont en quelque sorte ignorées - l'état final de la transition est immédiatement rendu.

Par exemple, étant donné ce CSS (préfixes omis ici):

.box { 
  opacity: 0;
  transition: all 2s;
  background-color: red;
  height: 100px;
  width: 100px;
}

.box.in { opacity: 1; }

L'opacité de cet élément sera immédiatement définie sur 1:

// Does not animate
var $a = $('<div>')
    .addClass('box a')
    .appendTo('#wrapper');
$a.addClass('in');

J'ai vu plusieurs façons de déclencher la transition pour obtenir le comportement attendu:

// Does animate
var $b = $('<div>')
    .addClass('box b')
    .appendTo('#wrapper');

setTimeout(function() {
    $('.b').addClass('in');
},0);

// Does animate
var $c = $('<div>')
    .addClass('box c')
    .appendTo('#wrapper');

$c[0]. offsetWidth = $c[0].offsetWidth
$c.addClass('in');

// Does animate
var $d = $('<div>')
    .addClass('box d')
    .appendTo('#wrapper');
$d.focus().addClass('in');

Les mêmes méthodes s'appliquent à la manipulation de Vanilla JS DOM - il ne s'agit pas d'un comportement spécifique à jQuery.

Modifier - J'utilise Chrome 35.

JSFiddle (inclut l'exemple de Vanilla JS).

  • Pourquoi les animations CSS immédiates sur les éléments ajoutés sont-elles ignorées?
  • Comment et pourquoi ces méthodes fonctionnent-elles?
  • Y a-t-il d'autres façons de le faire
  • Quelle est, le cas échéant, la solution préférée?
53
joews

La cause de la non-animation du nouvel élément ajouté est le traitement en différé par les navigateurs.

Lorsque l'élément est ajouté, il est nécessaire de refondre. La même chose s'applique à l'ajout de la classe. Cependant, lorsque vous faites les deux dans javascript single round , le navigateur tente d’optimiser le premier. Dans ce cas, il n'y a qu'une seule valeur de style (initiale et finale en même temps), de sorte qu'aucune transition ne se produira.

L’astuce setTimeout fonctionne, car elle retarde l’ajout de la classe à un autre tour javascript. Par conséquent, le moteur de rendu a deux valeurs à calculer, car il existe un point dans le temps lorsque la première est présentée à l’utilisateur.

Il existe une autre exception à la règle de mise en lot. Le navigateur doit calculer la valeur immédiate si vous essayez d'y accéder. Une de ces valeurs est offsetWidth. Lorsque vous y accédez, la refusion est déclenchée. Un autre est effectué séparément lors de l’affichage. Encore une fois, nous avons deux valeurs de style différentes, nous pouvons donc les interpoler dans le temps.

C’est vraiment l’une des rares occasions où ce comportement est souhaitable. La plupart du temps, l’accès aux propriétés génératrices de refusion entre les modifications DOM peut provoquer un ralentissement important.

La solution préférée peut varier d'une personne à l'autre, mais pour moi, l'accès de offsetWidth (ou getComputedStyle()) est le meilleur. Il existe des cas où setTimeout est déclenché sans recalcul des styles entre les deux. C'est un cas rare, principalement sur des sites chargés, mais cela arrive. Ensuite, vous ne recevrez pas votre animation. En accédant à n’importe quel style calculé, vous obligez le navigateur à le calculer.

93
Frizi

En utilisant jQuery, essayez ceci ( Un exemple ici. ):

var $a = $('<div>')
.addClass('box a')
.appendTo('#wrapper');
$a.css('opacity'); // added
$a.addClass('in');

En utilisant JavaScript javaScript, essayez ceci:

var e = document.createElement('div');
e.className = 'box e';
document.getElementById('wrapper').appendChild(e);
window.getComputedStyle(e).opacity; // added
e.className += ' in';

Brève idée:

Le getComputedStyle () efface toutes les modifications de style en attente et oblige le moteur de disposition à calculer l'état actuel de l'élément, d'où .css () fonctionne de manière similaire.

À propos de css()de jQuery site:

La méthode .css () est un moyen pratique d’obtenir une propriété de style à partir de premier élément assorti, en particulier à la lumière des différentes façons les navigateurs accèdent à la plupart de ces propriétés (la méthode getComputedStyle () dans les navigateurs standard par rapport aux propriétés currentStyle et runtimeStyle dans Internet Explorer) et les différents termes les navigateurs utilisent pour certaines propriétés.

Vous pouvez utiliser getComputedStyle()/css() au lieu de setTimeout. Aussi, vous pouvez lire cet article pour plus de détails, informations et exemples.

26
The Alpha

La solution de @ Frizi fonctionne, mais j'ai parfois constaté que getComputedStyle n'a pas fonctionné lorsque je modifie certaines propriétés d'un élément. Si cela ne fonctionne pas, vous pouvez essayer getBoundingClientRect() comme suit, ce qui, à mon avis, est à l'épreuve des balles:

Supposons que nous ayons un élément el, sur lequel nous voulons faire une transition opacity, mais el est display:none; opacity: 0:

el.style.display = 'block';
el.style.transition = 'opacity .5s linear';

// reflow
el.getBoundingClientRect();

// it transitions!
el.style.opacity = 1;
6
nikk wong

Veuillez utiliser le code ci-dessous, utilisez "focus ()"

Jquery

var $a = $('<div>')
    .addClass('box a')
    .appendTo('#wrapper');
$a.focus(); // focus Added
$a.addClass('in');

Javascript

var e = document.createElement('div');
e.className = 'box e';
document.getElementById('wrapper').appendChild(e).focus(); // focus Added
e.className += ' in';
5
Manu Janardhanan

Plutôt que d'essayer de forcer un repeint immédiat ou un calcul de style, j'ai essayé d'utiliser requestAnimationFrame() pour permettre au navigateur de peindre sur son prochain cadre disponible.

Dans Chrome + Firefox, le navigateur optimise trop le rendu, ce qui ne résout pas le problème (fonctionne dans Safari).

J'ai décidé de forcer manuellement un délai avec setTimeout(), puis d'utiliser requestAnimationFrame() pour laisser le navigateur Paint de manière responsable. Si l'ajout n'a pas peint avant l'expiration du délai, l'animation peut être ignorée, mais elle semble fonctionner de manière fiable.

setTimeout(function () {
    requestAnimationFrame(function () {
        // trigger the animation
    });
}, 20);

J'ai choisi 20 ms parce qu'il dépasse 1 frame à 60 ips (16,7 ms) et que certains navigateurs n'enregistrent pas les délais d'attente <5 ms.

Les doigts croisés qui devraient forcer l’animation à démarrer dans la prochaine image, puis à la démarrer de manière responsable lorsque le navigateur est prêt à peindre à nouveau.

2
Brendan Falkowski

Contrairement à Brendan , j’ai constaté que requestAnimationFrame() fonctionnait sous Chrome 63, Firefox 57, IE11 et Edge.

var div = document.createElement("div");
document.body.appendChild(div);

requestAnimationFrame(function () {
  div.className = "fade";
});
div {
  height: 100px;
  width: 100px;
  background-color: red;
}

.fade {
  opacity: 0;
  transition: opacity 2s;
}

0
James T

Je préfère requestAnimationFrame + setTimeout (voir this post ).

const child = document.createElement("div");
child.style.backgroundColor = "blue";
child.style.width = "100px";
child.style.height = "100px";
child.style.transition = "1s";

parent.appendChild(child);

requestAnimationFrame(() =>
  setTimeout(() => {
    child.style.width = "200px";
  })
);

Essayez-le ici .

0
pomber