2016-05-23 15 views
1

Ich versuche, eine Aktion zu erstellen, wo ein Div erstellt wird und sofort "schwebt" nach oben, bis es aus dem Bildschirm ist.Sofort Übergang nach Element ausgelöst

Um dies zu erreichen, versuche ich eine CSS transition zu verwenden, die vollständig von JavaScript (aufgrund von Einschränkungen in meinem Anwendungsfall) angetrieben wird.

Ein Problem tritt auf, wenn ich ein Element erstelle, ihm seine Übergangsstileigenschaften zuweise, und dann sofort versuche, den Übergang durch eine Stiländerung (top) zu starten.

Es sieht aus wie ein Timing-Problem passiert, wo die top Stiländerung vor dem Übergang feuern wird und damit einfach meine div Off-Screen sofort verschieben, anstatt tatsächlich den Übergang durchzuführen.

Hier ist ein vereinfachtes Beispiel:

var 
    defaultHeight = 50, 
    iCount = 0, 
    aColors = ['red', 'orange', 'yellow', 'green', 'blue', 'purple']; 

function createBlock() { 
    var testdiv = document.createElement('div'); 
    testdiv.className = 'testdiv'; 
    document.body.appendChild(testdiv); 
    testdiv.style.left = '50%'; 
    testdiv.style.backgroundColor = aColors[iCount % aColors.length]; 
    testdiv.style.width = defaultHeight + 'px'; 
    testdiv.style.height = defaultHeight + 'px'; 
    testdiv.style.fontSize = '30px'; 
    testdiv.style.textAlign = 'center'; 
    testdiv.innerHTML = iCount; 
    testdiv.style.top = '500px'; 
    testdiv.style.position = 'absolute'; 
    iCount++; 
    return testdiv; 
} 

document.getElementById('go').onclick = function() { 
    var testdiv = createBlock(); 

    testdiv.style.transition = "top 2.0s linear 0s"; 

    setTimeout(function() { 
     testdiv.style.top = (defaultHeight*-2) + 'px'; 
    }, 0); // <- change this to a higher value to see the transition always occur 
}; 

Wenn die Schaltfläche „Go“ (see JSBin) nur schnell der div (vermutlich aufgrund des oben beschriebenen Zeitproblem) sporadisch angeklickt wird angezeigt.

Wenn Sie den Verzögerungswert von setTimeout erhöhen, können Sie sehen, dass der Übergang fast immer funktioniert.

Gibt es eine Möglichkeit, einen Übergang direkt nach dem Erstellen eines Elements deterministisch zu starten (ohne auf eine Verzögerung zurückgreifen zu müssen)?

+0

Sie müssen die animierte (neu) Top-Wert in einer Klasse hinzuzufügen und diese Klasse zuweisen stattdessen oder auch der Übergang nicht weiß, zwischen denen oben, da diese zu animieren Wert überschreibt den ersten. – LGSon

+0

@LGSon Das neu erstellte div erhält einen anfänglichen 'top'-Wert von' 500px '... warum müsste das in einer CSS-Klasse definiert werden (außer als Best-Practice)? –

+0

Misread Ihre Frage ein wenig ... wird eine Antwort in einer Sekunde posten. – LGSon

Antwort

3

Für ein Übergang braucht man zwei verschiedene Zustände aka. ein Wechsel.

Zwischen zwei Render-Zyklen-Stilen werden nur überschrieben. Sie werden nur angewendet, wenn der Knoten (neu) gerendert wird.
Wenn also setTimeout() ausgelöst wird, bevor die "alten" Stile angewendet wurden, werden die Stlyes nur überschrieben und Ihre Knoten werden mit dem Zielstil gerendert.

Afaik. Die meisten (Desktop-) Browser streben eine Framerate von 60 fps an, die ein Intervall von 16,7 ms bildet. Daher wird setTimeout (fn, 0) höchstwahrscheinlich vorher ausgelöst.

Sie können entweder die Zeitüberschreitung erhöhen, wie Sie erwähnt haben (ich würde mindestens 50ms empfehlen), oder Sie können das Rendering des Knotens auslösen/erzwingen; zum Beispiel, indem man nach seiner Größe fragt.

Um die Größe des Knotens zu ermitteln, muss der Browser zuerst alle Stile auf den Knoten anwenden, um zu wissen, wie sie ihn beeinflussen.
Auch der Kontext ist wichtig für CSS, also muss der Node irgendwo zum DOM hinzugefügt werden, damit der Browser die endlich berechneten Styles erhalten kann.

Lange Antwort kurz:

document.getElementById('go').onclick = function() { 
    var testdiv = createBlock(); 
    testdiv.style.transition = "top 2.0s linear 0s"; 

    testdiv.scrollWidth; //trigger rendering 

    //JsBin brags that you don't do anything with the value. 
    //and some minifyer may think it is irrelevant and remove it. 
    //so, increment it (`++`); the value is readonly, you can do no harm by that. 
    //but the mere expression, as shown, already triggers the rendering. 

    //now set the target-styles for the transition 
    testdiv.style.top = (defaultHeight*-2) + 'px'; 
}; 
+0

Funktioniert gut, +1 ... jetzt habe ich etwas neues gelernt :) – LGSon

+0

Das ist ein Verhalten, das Sie absichtlich nutzen können wie in diesem Fall, oder es kann einige erhebliche Auswirkungen auf Ihren Code haben, einfach durch die Reihenfolge, in der Sie Dinge in Ihrem Code tun. In Bezug auf die Leistung ist das Teuerste, was Sie tun können, ein unnötiges erneutes Rendern auszulösen. indem Sie einige className auf einem Knoten ändern und dann nach etwas fragen, das vom Stil auf einem der untergeordneten Knoten beeinflusst wird. wie die Position zum Beispiel. – Thomas

+0

Also, was Sie sagen, ist in diesem Fall, die Verwendung von 'Animation' wird Leistung gegen trigger ein Rendern mit 'Übergang' zu gewinnen? – LGSon

1

Eine alternative animation

verwenden, wenn Sie die keyframes dynamisch erstellen möchten, mit Hilfe von Javascript, hier ist ein schöner Beitrag, dass zeigt: https://stackoverflow.com/questions/10342494/set-webkit-keyframes-values-using-javascript-variable

var 
 
    defaultHeight = 50, 
 
    iCount = 0, 
 
    aColors = ['red', 'orange', 'yellow', 'green', 'blue', 'purple']; 
 

 
function createBlock() { 
 
    var testdiv = document.createElement('div'); 
 
    testdiv.className = 'testdiv'; 
 
    document.body.appendChild(testdiv); 
 
    testdiv.style.left = '50%'; 
 
    testdiv.style.backgroundColor = aColors[iCount % aColors.length]; 
 
    testdiv.style.width = defaultHeight + 'px'; 
 
    testdiv.style.height = defaultHeight + 'px'; 
 
    testdiv.style.fontSize = '30px'; 
 
    testdiv.style.textAlign = 'center'; 
 
    testdiv.innerHTML = iCount; 
 
    testdiv.style.top = '300px'; 
 
    testdiv.style.position = 'absolute'; 
 
    // you can preset the animation here as well 
 
    //testdiv.style.animation = 'totop 2.0s linear forwards'; 
 
    iCount++; 
 
    return testdiv; 
 
} 
 

 
document.getElementById('go').onclick = function() { 
 
    var testdiv = createBlock(); 
 
    testdiv.style.animation = 'totop 2.0s linear forwards'; 
 
};
@keyframes totop { 
 
    100% { top: -100px; } 
 
}
<button id="go">go</button>

+0

Das sieht gut aus, aber die Verwendung von Animationen erfordert die Definition der Keyframes, die mit CSS gesetzt werden müssen. Ich suche nach einer All-JS-Lösung, bei der ich nichts Neues in die Stile des Dokuments einfügen muss. Ich verstehe, dass dies nicht Standard ist. Weißt du, warum Animationen hier funktionieren, aber keine Übergänge? –

+0

@ Jonathan.Brink Die Animation hat sowohl einen Start- als auch einen Endwert, wenn sie erstellt wird und der Übergang nicht, daher muss der Übergang zuerst gezeichnet werden, bevor ihm ein zweiter oberer Wert zugewiesen wird, der den Übergang startet. Die Verzögerung in Ihrem Fall macht dies möglich. – LGSon

+0

... ah "der Übergang muss zuerst gezogen werden" kann der Schlüssel zum Verständnis des Problems sein ... was meinst du "muss zuerst gezogen werden"? Sprichst du über einen Schritt, der vor dem eigentlichen Beginn des Übergangs stattfindet (durch die Stiländerung)? –