Da ich kürzlich einige Seitenanimationseffekte erstellt habe, hatte ich dieses Mal während des Prozesses einige Probleme. Außerdem habe ich vor langer Zeit einen guten Blog über Animation gelesen Need Know“ aus Ten Years of Traces, also habe ich über einige Fragen zu den Grundprinzipien der Animation nachgedacht, wie zum Beispiel diesen Artikel. Diese Frage kann einfach oder sehr einfach sein. In dem zuvor erwähnten Blog finden Sie eine weitere, detailliertere Methode.
Bei der objektiven Bewegung von Objekten können wir am Beispiel einer gleichmäßigen linearen Bewegung Geschwindigkeits- und Zeitkurven oder Verschiebungs- und Zeitkurven verwenden, um die Bewegung von Objekten gleichzeitig zu beschreiben:
Ob die Beziehung zwischen Geschwindigkeit und Zeit oder Verschiebung und Zeit zur Beschreibung der linearen Bewegung eines objektiven Objekts verwendet wird, der Zustand des Objekts ist konsistent. Dies liegt daran, dass die Bewegung eines objektiven Objekts immer erfolgt folgt einem objektiven Weg, der von Menschen nicht verändert werden kann, und an jedem Punkt der Zeitachse gibt es immer eine entsprechende Geschwindigkeit und Verschiebung.
In der Webanimation wird sie zwar auch als Bewegung dargestellt, wir können sie jedoch nicht mit den Bewegungsregeln objektiver Objekte beschreiben. Ich denke, der Hauptgrund ist, dass die Essenz einer Animation nicht in der Bewegung liegt, sondern nur in einer augenblicklichen Änderung des Elementzustands basierend auf dem Timer. Nehmen Sie als Beispiel den Animationseffekt des horizontalen gleichmäßigen Versatzes eines Elements. Um diese Animation zu erreichen, müssen Sie nur einen Timer verwenden, um den x-Achsen-Versatz des Elements in einem festen Zeitintervall zurückzusetzen . Wie folgt:
T1~t6 in der Abbildung stellen die Zeit dar, zu der die Timer-Rückruffunktion ausgeführt wird. In diesem Fall ändert sich die Versatzposition des Elements bei jeder Ausführung des Timers, die Versatzposition des Elements ändert sich jedoch nicht zwischen zwei benachbarten Ausführungszeiten. Die Animation, die wir sehen, ist nur darauf zurückzuführen, dass das Timer-Intervall zu kurz ist und wir den Prozess dieses Zeitraums nicht visuell wahrnehmen können. Wenn wir das Timer-Intervall auf lange genug verlängern, können wir den Status des Elements während des Intervalls sehen.
Da Animation keine Bewegung ist, können wir beim Versuch, einige Animationsprozesse zu verstehen, nicht in Bewegungsgesetzen denken. Wie sollen wir beispielsweise den Zustand in dem Moment verstehen, in dem die Animation stoppt? Nehmen wir als Beispiel den zuvor erwähnten Animationseffekt: Wenn der Timer gelöscht wird, stoppt die Animation sofort. Für das Element wird seine Animationsgeschwindigkeit plötzlich 0. Wenn wir es mit der Bewegung von Zielobjekten vergleichen, können Sie es nehmen Es ist selbstverständlich, dass die Animation eines Elements zunächst verlangsamt werden sollte, bevor sie stoppen kann. Wenn Sie so denken, werden Sie das Prinzip eines plötzlichen Stopps nicht verstehen können, wenn die Animation eines Elements stoppt. Wenn wir jedoch über das Wesentliche der Animation nachdenken, ist es leicht zu verstehen, da der Timer das einzige Element ist, das den Status des Elements während des Animationsprozesses ändert. Wenn der Timer nicht funktioniert, gibt es keine externe Funktion Kraft wird verwendet, um den Zustand der Elemente zu ändern. Wie kann sie sich trotzdem bewegen?
Obwohl Animation keine Bewegung ist, hoffen wir dennoch, eine Möglichkeit zu finden, die Geschwindigkeit der Animation gut zu steuern, um einen Animationseffekt zu erzeugen, der flüssiger und näher an der objektiven Welt ist. Wenn man von Geschwindigkeit spricht, denkt man leicht an Geschwindigkeit, denn bei der Bewegung objektiver Objekte ist Geschwindigkeit das Element, mit dem die Bewegungsgeschwindigkeit beschrieben wird. Und die Verwendung von Geschwindigkeitsregeln zur Steuerung der Animationsgeschwindigkeit scheint leicht zu verstehen und umzusetzen. Um das vorherige Beispiel konkreter zu machen: Wenn wir den Animationseffekt erzielen möchten, dass sich ein Element innerhalb einer Sekunde mit gleichmäßiger Geschwindigkeit um 120 Pixel nach rechts verschiebt, müssen wir nur einen Timer verwenden, um das Element zu steuern, um das es um einen festen Betrag verschoben werden soll Jedes Mal rechts. Der Offset, der dem Element bei jeder Ausführung des Timers hinzugefügt wird, ist die Geschwindigkeit, mit der wir die Animation steuern. Wenn wir 16 ms als Timer-Intervall verwenden, kann die Geschwindigkeit dieser Animation wie folgt berechnet werden: 120 Pixel / (1000 ms / 16 ms) (ungefähr gleich 2 Pixel), das heißt, solange der Timer das Element nach rechts versetzt Bei jeder Ausführung kann 2px den gewünschten Effekt erzielen. Die einfache Code-Implementierung lautet wie folgt:
<span style="color: #0000ff"><span style="color: #ff00ff">doctype html</span><span style="color: #0000ff">></span> <span style="color: #0000ff"><span style="color: #800000">html </span><span style="color: #ff0000">lang</span><span style="color: #0000ff">="en"</span><span style="color: #0000ff">></span> <span style="color: #0000ff"><span style="color: #800000">head</span><span style="color: #0000ff">></span> <span style="color: #0000ff"><span style="color: #800000">meta </span><span style="color: #ff0000">charset</span><span style="color: #0000ff">="UTF-8"</span><span style="color: #0000ff">></span> <span style="color: #0000ff"><span style="color: #800000">title</span><span style="color: #0000ff">></span>Document<span style="color: #0000ff"></span><span style="color: #800000">title</span><span style="color: #0000ff">></span> <span style="color: #0000ff"></span><span style="color: #800000">head</span><span style="color: #0000ff">></span> <span style="color: #0000ff"><span style="color: #800000">body</span><span style="color: #0000ff">></span> <span style="color: #0000ff"><span style="color: #800000">div </span><span style="color: #ff0000">id</span><span style="color: #0000ff">="box"</span><span style="color: #ff0000"> style</span><span style="color: #0000ff">="width: 100px;height: 100px;background-color: goldenrod"</span><span style="color: #0000ff">></span> <span style="color: #0000ff"></span><span style="color: #800000">div</span><span style="color: #0000ff">></span> <span style="color: #0000ff"><span style="color: #800000">br</span><span style="color: #0000ff">></span> <span style="color: #0000ff"><span style="color: #800000">button </span><span style="color: #ff0000">type</span><span style="color: #0000ff">="button"</span><span style="color: #ff0000"> onclick</span><span style="color: #0000ff">="start()"</span><span style="color: #0000ff">></span>开始<span style="color: #0000ff"></span><span style="color: #800000">button</span><span style="color: #0000ff">></span> <span style="color: #0000ff"></span><span style="color: #800000">body</span><span style="color: #0000ff">></span> <span style="color: #0000ff"><span style="color: #800000">script</span><span style="color: #0000ff">></span> <span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> box </span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5"> document.getElementById(</span><span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">box</span><span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">); </span><span style="color: #0000ff; background-color: #f5f5f5">function</span><span style="color: #000000; background-color: #f5f5f5"> start() { </span><span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> duration </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #000000; background-color: #f5f5f5">1000</span><span style="color: #000000; background-color: #f5f5f5">;</span><span style="color: #008000; background-color: #f5f5f5">//</span><span style="color: #008000; background-color: #f5f5f5">动画时长</span> <span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> s </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #000000; background-color: #f5f5f5">120</span><span style="color: #000000; background-color: #f5f5f5">;</span><span style="color: #008000; background-color: #f5f5f5">//</span><span style="color: #008000; background-color: #f5f5f5">总的偏移量</span> <span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> cur_s </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #000000; background-color: #f5f5f5">0</span><span style="color: #000000; background-color: #f5f5f5">;</span><span style="color: #008000; background-color: #f5f5f5">//</span><span style="color: #008000; background-color: #f5f5f5">当前偏移总量</span> <span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> p </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #000000; background-color: #f5f5f5">16</span><span style="color: #000000; background-color: #f5f5f5">;</span><span style="color: #008000; background-color: #f5f5f5">//</span><span style="color: #008000; background-color: #f5f5f5">定时器间隔</span> <span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> speed </span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5"> s </span><span style="color: #000000; background-color: #f5f5f5">/</span><span style="color: #000000; background-color: #f5f5f5"> (duration </span><span style="color: #000000; background-color: #f5f5f5">/</span><span style="color: #000000; background-color: #f5f5f5"> p);</span><span style="color: #008000; background-color: #f5f5f5">//</span><span style="color: #008000; background-color: #f5f5f5">速度</span> <span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> count </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #000000; background-color: #f5f5f5">0</span><span style="color: #000000; background-color: #f5f5f5">; </span><span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> start_time </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #0000ff; background-color: #f5f5f5">new</span><span style="color: #000000; background-color: #f5f5f5"> Date().getTime(); </span><span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> timer </span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5"> setInterval(</span><span style="color: #0000ff; background-color: #f5f5f5">function</span><span style="color: #000000; background-color: #f5f5f5">(){ </span><span style="color: #0000ff; background-color: #f5f5f5">if</span><span style="color: #000000; background-color: #f5f5f5">(cur_s </span><span style="color: #000000; background-color: #f5f5f5">>=</span><span style="color: #000000; background-color: #f5f5f5"> s) { clearInterval(timer); console.log(</span><span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">动画运行时间(ms): </span><span style="color: #000000; background-color: #f5f5f5">'</span> <span style="color: #000000; background-color: #f5f5f5">+</span><span style="color: #000000; background-color: #f5f5f5"> (</span><span style="color: #0000ff; background-color: #f5f5f5">new</span><span style="color: #000000; background-color: #f5f5f5"> Date().getTime() </span><span style="color: #000000; background-color: #f5f5f5">-</span><span style="color: #000000; background-color: #f5f5f5"> start_time)); </span><span style="color: #0000ff; background-color: #f5f5f5">return</span><span style="color: #000000; background-color: #f5f5f5">; } count</span><span style="color: #000000; background-color: #f5f5f5">++</span><span style="color: #000000; background-color: #f5f5f5">; cur_s </span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5"> speed </span><span style="color: #000000; background-color: #f5f5f5">*</span><span style="color: #000000; background-color: #f5f5f5"> count; box.style.transform </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">translateX(</span><span style="color: #000000; background-color: #f5f5f5">'</span> <span style="color: #000000; background-color: #f5f5f5">+</span><span style="color: #000000; background-color: #f5f5f5"> cur_s </span><span style="color: #000000; background-color: #f5f5f5">+</span> <span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">px)</span><span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">; },p); } </span><span style="color: #0000ff"></span><span style="color: #800000">script</span><span style="color: #0000ff">></span> <span style="color: #0000ff"></span><span style="color: #800000">html</span><span style="color: #0000ff">></span></span></span></span></span></span></span></span></span></span></span>
在浏览器中运行以上代码,动画效果肯定是跟预期一致的,而且动画的实际执行时间也与规定的时长相差很小:
至于为什么不完全等于1000ms,那是因为多的那20多毫秒都耗费在了代码执行上。
通过这个例子,看起来,我们用速度去控制动画的思路还比较可行。事实上,这种思路是很有局限性的,我不是说它不行,只是说局限性,就是只能用于小部分的场合,而不能适用更广泛的动画效果中。为什么呢,原因有多个方面。
先从定时器说起。
定时器给了我们一种通过代码的方式来管理时间轴,但是这个时间轴与客观时间轴是有差别的。假如我们把一个动画的定时器间隔放大,放大到1000ms,让这个定时器执行10次,定时器执行的真实时间间隔会等于1000ms吗?
<span style="color: #0000ff"><span style="color: #800000">script</span><span style="color: #0000ff">></span> <span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> start </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #0000ff; background-color: #f5f5f5">new</span><span style="color: #000000; background-color: #f5f5f5"> Date().getTime(),count </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #000000; background-color: #f5f5f5">1</span><span style="color: #000000; background-color: #f5f5f5">; </span><span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> timer </span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5"> setInterval(</span><span style="color: #0000ff; background-color: #f5f5f5">function</span><span style="color: #000000; background-color: #f5f5f5">(){ </span><span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> end </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #0000ff; background-color: #f5f5f5">new</span><span style="color: #000000; background-color: #f5f5f5"> Date().getTime(); console.log(</span><span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">第</span><span style="color: #000000; background-color: #f5f5f5">'</span> <span style="color: #000000; background-color: #f5f5f5">+</span><span style="color: #000000; background-color: #f5f5f5"> count</span><span style="color: #000000; background-color: #f5f5f5">++</span> <span style="color: #000000; background-color: #f5f5f5">+</span> <span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">次执行,间隔:</span><span style="color: #000000; background-color: #f5f5f5">'</span> <span style="color: #000000; background-color: #f5f5f5">+</span><span style="color: #000000; background-color: #f5f5f5"> (end </span><span style="color: #000000; background-color: #f5f5f5">-</span><span style="color: #000000; background-color: #f5f5f5"> start)); start </span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5"> end; </span><span style="color: #0000ff; background-color: #f5f5f5">if</span><span style="color: #000000; background-color: #f5f5f5">(count </span><span style="color: #000000; background-color: #f5f5f5">==</span> <span style="color: #000000; background-color: #f5f5f5">11</span><span style="color: #000000; background-color: #f5f5f5">) { clearInterval(timer); } },</span><span style="color: #000000; background-color: #f5f5f5">1000</span><span style="color: #000000; background-color: #f5f5f5">); </span><span style="color: #0000ff"></span><span style="color: #800000">script</span><span style="color: #0000ff">></span></span>
以上代码模拟了一个动画,并且放大了动画的时间间隔,如果把它拿到浏览器中执行,我们会得到下面类似的结果:
从这个结果可以看出,虽然定时器的间隔设置为了1000ms,但是实际的执行间隔却只能说在1000左右浮动。这是很正常的,假如我们把操作系统的时间看成是客观的时间轴,那么浏览器里面定时器构建的时间轴只能是一个尽可能的接近客观时间轴的模拟时间轴。操作系统的状态,浏览器的状态,定时器内外代码的执行时间都会影响这根时间轴与客观时间轴的差距,只考虑浏览器内部,定时器内外的代码执行时间越长,这其中的差距越大。因为上面的代码是在一个很简单的网页中测试出来的,所以定时器的实际间隔与客观时间的偏差很小,要是一个页面内容比较多的时候,这个偏差一定会比现在的大。
时间轴的不稳定性,会直接导致速度的不稳定性,也就是说匀速运动都无法达到理想状态,更别说其它复杂的变速运动了。
单从这点来说,不管用什么方式控制运动,都会存在这个问题,所以它还并不能完全说明速度控制动画的根本问题所在。这个根本问题在于无法确保动画能够按照规定的时长完成。在上面的例子的基础上,我们想办法把定时器的时间轴与客观时间差的偏差放大,这个不难办到,只要在定时器执行过程中,加入一些耗时任务即可,代码如下:
<span style="color: #0000ff"><span style="color: #800000">script</span><span style="color: #0000ff">></span> <span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> start </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #0000ff; background-color: #f5f5f5">new</span><span style="color: #000000; background-color: #f5f5f5"> Date().getTime(), prev </span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5"> start, count </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #000000; background-color: #f5f5f5">1</span><span style="color: #000000; background-color: #f5f5f5">; </span><span style="color: #008000; background-color: #f5f5f5">//</span><span style="color: #008000; background-color: #f5f5f5">在动画模拟的第2和第3秒之间插入一个耗时任务</span> <span style="color: #000000; background-color: #f5f5f5"> setTimeout(</span><span style="color: #0000ff; background-color: #f5f5f5">function</span><span style="color: #000000; background-color: #f5f5f5"> () { </span><span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> i </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #000000; background-color: #f5f5f5">0</span><span style="color: #000000; background-color: #f5f5f5">; </span><span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> cur </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #0000ff; background-color: #f5f5f5">new</span><span style="color: #000000; background-color: #f5f5f5"> Date().getTime(); console.log(</span><span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">耗时任务开始,距动画开始时间:</span><span style="color: #000000; background-color: #f5f5f5">'</span> <span style="color: #000000; background-color: #f5f5f5">+</span><span style="color: #000000; background-color: #f5f5f5"> (cur</span><span style="color: #000000; background-color: #f5f5f5">-</span><span style="color: #000000; background-color: #f5f5f5">start)); </span><span style="color: #0000ff; background-color: #f5f5f5">while</span><span style="color: #000000; background-color: #f5f5f5"> (</span><span style="color: #000000; background-color: #f5f5f5">++</span><span style="color: #000000; background-color: #f5f5f5">i </span><span style="color: #000000; background-color: #f5f5f5"> <span style="color: #000000; background-color: #f5f5f5">3000000000</span><span style="color: #000000; background-color: #f5f5f5">); </span><span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> cur2 </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #0000ff; background-color: #f5f5f5">new</span><span style="color: #000000; background-color: #f5f5f5"> Date().getTime(); console.log(</span><span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">耗时任务结束,距动画开始时间:</span><span style="color: #000000; background-color: #f5f5f5">'</span> <span style="color: #000000; background-color: #f5f5f5">+</span><span style="color: #000000; background-color: #f5f5f5"> (cur2</span><span style="color: #000000; background-color: #f5f5f5">-</span><span style="color: #000000; background-color: #f5f5f5">start) </span><span style="color: #000000; background-color: #f5f5f5">+</span> <span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">,耗时:</span><span style="color: #000000; background-color: #f5f5f5">'</span> <span style="color: #000000; background-color: #f5f5f5">+</span><span style="color: #000000; background-color: #f5f5f5"> (cur2</span><span style="color: #000000; background-color: #f5f5f5">-</span><span style="color: #000000; background-color: #f5f5f5">cur)); }, </span><span style="color: #000000; background-color: #f5f5f5">2400</span><span style="color: #000000; background-color: #f5f5f5">); </span><span style="color: #008000; background-color: #f5f5f5">//</span><span style="color: #008000; background-color: #f5f5f5">模拟一个动画</span> <span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> timer </span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5"> setInterval(</span><span style="color: #0000ff; background-color: #f5f5f5">function</span><span style="color: #000000; background-color: #f5f5f5"> () { </span><span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> end </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #0000ff; background-color: #f5f5f5">new</span><span style="color: #000000; background-color: #f5f5f5"> Date().getTime(); console.log(</span><span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">第</span><span style="color: #000000; background-color: #f5f5f5">'</span> <span style="color: #000000; background-color: #f5f5f5">+</span><span style="color: #000000; background-color: #f5f5f5"> count</span><span style="color: #000000; background-color: #f5f5f5">++</span> <span style="color: #000000; background-color: #f5f5f5">+</span> <span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">次执行,间隔:</span><span style="color: #000000; background-color: #f5f5f5">'</span> <span style="color: #000000; background-color: #f5f5f5">+</span><span style="color: #000000; background-color: #f5f5f5"> (end </span><span style="color: #000000; background-color: #f5f5f5">-</span><span style="color: #000000; background-color: #f5f5f5"> prev)); prev </span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5"> end; </span><span style="color: #0000ff; background-color: #f5f5f5">if</span><span style="color: #000000; background-color: #f5f5f5"> (count </span><span style="color: #000000; background-color: #f5f5f5">==</span> <span style="color: #000000; background-color: #f5f5f5">11</span><span style="color: #000000; background-color: #f5f5f5">) { clearInterval(timer); } }, </span><span style="color: #000000; background-color: #f5f5f5">1000</span><span style="color: #000000; background-color: #f5f5f5">); </span><span style="color: #0000ff"></span><span style="color: #800000">script</span><span style="color: #0000ff">></span></span></span>
把以上代码在浏览器中运行,我们可以得到下面的类似结果:
根据以上结果中的时间范围,我们把这个例子的整个过程转换为时间轴示意图的话,就能看得更清晰了:
在这个图中,忽略了临界点之间的微小差距,因为只要观察那些大的差距,就能发现问题。结合前面的代码跟示意图,我们能看出:
由于有耗时任务的加入,导致动画的实际执行总时间接近于12s,比规定的动画时长多出整整2s。
虽然说从上面的图中也能看到另外一个问题,就是动画第三次执行的时间间隔被延长为3.67,而第四次执行的时间间隔被缩短为0.33s,会导致动画在这个时间段左右会看到不连贯不流畅的效果,但是这个问题不管用什么样的方式都会存在,只要有其它耗时任务在处理,动画定时器的回调就必须排队等待耗时任务完成才能执行。
无法控制动画在规定时长内完成,是不能用速度与时间的关系去实现动画的最重要的原因。
综上所述,为什么不用速度去控制动画有两个原因:
一是因为动画的时间轴的不稳定性(耗时任务会加大这种不稳定性),导致速度的变化规律很难把握。即使是匀速动画,我们也要考虑定时器的间隔,动画的偏移量,动画的时长三个参数才能计算出一个平均速度。如果是变速动画呢,比如我们想要一个动画先加速再匀速后减速,这种动画快慢的控制要求显然就无法轻易实现了。
二就是因为无法控制动画时长。
那么用什么样的方式来控制动画,就能够达到我们想要的轻易控制动画速度的目标呢?
用偏移量(位移)跟时间的关系吗?显然也是不行的,因为仅仅是单纯的速度控制改变为位移控制,并不会从根本上解决问题,因为速度与时间的关系还有位移与时间的关系是等价的。
速度无法控制动画时长的原因在于,由于已知的动画偏移量跟动画时长,导致动画定时器的执行次数也是固定的!所以只要某些次数定时器的实际执行时间超过理想的执行间隔,就会拉长动画时间轴跟客观时间轴的差距,就像上面示意图所看到的那样。
真正能解决动画时长的控制问题在于我们一定要用客观时间轴去控制动画。这个能做到吗?当然是可以的,来看看正确实现一个动画的方式,还是以前面那个小方块往右移动的动画为例,代码修改如下:
<span style="color: #0000ff"><span style="color: #ff00ff">doctype html</span><span style="color: #0000ff">></span> <span style="color: #0000ff"><span style="color: #800000">html </span><span style="color: #ff0000">lang</span><span style="color: #0000ff">="en"</span><span style="color: #0000ff">></span> <span style="color: #0000ff"><span style="color: #800000">head</span><span style="color: #0000ff">></span> <span style="color: #0000ff"><span style="color: #800000">meta </span><span style="color: #ff0000">charset</span><span style="color: #0000ff">="UTF-8"</span><span style="color: #0000ff">></span> <span style="color: #0000ff"><span style="color: #800000">title</span><span style="color: #0000ff">></span>Document<span style="color: #0000ff"></span><span style="color: #800000">title</span><span style="color: #0000ff">></span> <span style="color: #0000ff"></span><span style="color: #800000">head</span><span style="color: #0000ff">></span> <span style="color: #0000ff"><span style="color: #800000">body</span><span style="color: #0000ff">></span> <span style="color: #0000ff"><span style="color: #800000">div </span><span style="color: #ff0000">id</span><span style="color: #0000ff">="box"</span><span style="color: #ff0000"> style</span><span style="color: #0000ff">="width: 100px;height: 100px;background-color: goldenrod"</span><span style="color: #0000ff">></span> <span style="color: #0000ff"></span><span style="color: #800000">div</span><span style="color: #0000ff">></span> <span style="color: #0000ff"><span style="color: #800000">br</span><span style="color: #0000ff">></span> <span style="color: #0000ff"><span style="color: #800000">button </span><span style="color: #ff0000">type</span><span style="color: #0000ff">="button"</span><span style="color: #ff0000"> onclick</span><span style="color: #0000ff">="start()"</span><span style="color: #0000ff">></span>开始<span style="color: #0000ff"></span><span style="color: #800000">button</span><span style="color: #0000ff">></span> <span style="color: #0000ff"></span><span style="color: #800000">body</span><span style="color: #0000ff">></span> <span style="color: #0000ff"><span style="color: #800000">script</span><span style="color: #0000ff">></span> <span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> box </span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5"> document.getElementById(</span><span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">box</span><span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">); </span><span style="color: #0000ff; background-color: #f5f5f5">function</span><span style="color: #000000; background-color: #f5f5f5"> start() { </span><span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> duration </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #000000; background-color: #f5f5f5">1000</span><span style="color: #000000; background-color: #f5f5f5">;</span><span style="color: #008000; background-color: #f5f5f5">//</span><span style="color: #008000; background-color: #f5f5f5">动画时长</span> <span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> s </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #000000; background-color: #f5f5f5">120</span><span style="color: #000000; background-color: #f5f5f5">;</span><span style="color: #008000; background-color: #f5f5f5">//</span><span style="color: #008000; background-color: #f5f5f5">总的偏移量</span> <span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> start_time </span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5"> Date.now(); </span><span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> timer </span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5"> setInterval(</span><span style="color: #0000ff; background-color: #f5f5f5">function</span><span style="color: #000000; background-color: #f5f5f5">(){ </span><span style="color: #008000; background-color: #f5f5f5">//</span><span style="color: #008000; background-color: #f5f5f5">percent表示动画的进程</span> <span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> percent </span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5"> (Date.now() </span><span style="color: #000000; background-color: #f5f5f5">-</span><span style="color: #000000; background-color: #f5f5f5"> start_time) </span><span style="color: #000000; background-color: #f5f5f5">/</span><span style="color: #000000; background-color: #f5f5f5"> duration; </span><span style="color: #0000ff; background-color: #f5f5f5">if</span><span style="color: #000000; background-color: #f5f5f5">(percent </span><span style="color: #000000; background-color: #f5f5f5">>=</span> <span style="color: #000000; background-color: #f5f5f5">1.0</span><span style="color: #000000; background-color: #f5f5f5">) { percent </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #000000; background-color: #f5f5f5">1</span><span style="color: #000000; background-color: #f5f5f5">; clearInterval(timer); console.log(</span><span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">动画运行时间(ms): </span><span style="color: #000000; background-color: #f5f5f5">'</span> <span style="color: #000000; background-color: #f5f5f5">+</span><span style="color: #000000; background-color: #f5f5f5"> (</span><span style="color: #0000ff; background-color: #f5f5f5">new</span><span style="color: #000000; background-color: #f5f5f5"> Date().getTime() </span><span style="color: #000000; background-color: #f5f5f5">-</span><span style="color: #000000; background-color: #f5f5f5"> start_time)); } box.style.transform </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">translateX(</span><span style="color: #000000; background-color: #f5f5f5">'</span> <span style="color: #000000; background-color: #f5f5f5">+</span><span style="color: #000000; background-color: #f5f5f5"> (Math.floor(s </span><span style="color: #000000; background-color: #f5f5f5">*</span><span style="color: #000000; background-color: #f5f5f5"> percent)) </span><span style="color: #000000; background-color: #f5f5f5">+</span> <span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">px)</span><span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">; },</span><span style="color: #000000; background-color: #f5f5f5">16</span><span style="color: #000000; background-color: #f5f5f5">); } </span><span style="color: #0000ff"></span><span style="color: #800000">script</span><span style="color: #0000ff">></span> <span style="color: #0000ff"></span><span style="color: #800000">html</span><span style="color: #0000ff">></span></span></span></span></span></span></span></span></span></span></span>
Die tatsächlichen Ausführungsergebnisse sind wie folgt:
Als nächstes fassen wir zusammen, wie das geht. Zunächst können Sie im Code sehen, dass wir diese Methode verwenden
Das Konzept des Animationsprozesses wird eingeführt und der Abschluss der Animation wird durch den Animationsprozess gesteuert:
Da der prozentuale Animationsprozess auf der objektiven Zeitachse basiert, können wir sicherstellen, dass die Animation innerhalb der angegebenen Zeit abgeschlossen werden kann, und es besteht nicht mehr das Problem, dass die Animationsausführungszeit verlängert wird, wenn die Geschwindigkeitssteuerungsanimation ausgeführt wird gebraucht. . (Wenn wir während der Animationsausführung eine sehr zeitaufwändige Aufgabe hinzufügen, kann natürlich keine Animation innerhalb der angegebenen Zeit abgeschlossen werden.)
Wenn wir uns dann mit dem Versatz der Animation befassen, müssen wir nur den gesamten Versatz * des Animationsprozesses hinzufügen, um den Versatz der aktuellen Ausführungszeit zu erhalten.
Wenn der Animationsfortschritt schließlich 1 ist, endet die Animation und das Element wird auf den durch die Animation angegebenen Gesamtoffset gesetzt.
Im Allgemeinen wandelt diese Methode die Beziehung zwischen Verschiebung und Zeit in die Beziehung zwischen Versatz und Animationsprozess um . Durch den Animationsprozess können Sie gleichzeitig die Animationsdauer und den Abschluss des Animationsversatzes steuern.
Noch wichtiger ist, dass die Beziehung zwischen Offset und Animationsprozess durch objektive Bewegungsgesetze abgeleitet werden kann:
Da es sich im obigen Beispiel beispielsweise um eine Animation mit konstanter Geschwindigkeit handelt, lautet die Regel:
Animationsprozess p = t / T; (t = Date.now() – start_time ; T = Dauer)
Offset Sp = S * p; (S ist der Gesamtoffset; Sp ist der aktuelle Offset)
Wenn es sich um andere Animationen handelt, wie z. B. eine Animation mit gleichmäßiger Beschleunigung, eine Animation mit gleichmäßiger Verzögerung und eine kreisförmige Animation, können wir auch ähnliche Regeln erhalten. Darüber hinaus ist die Berechnungsmethode des Animationsprozesses für alle Animationseffekte gleich. Der einzige Unterschied besteht in der Beziehung zwischen Offset und Animationsprozess:
Gleichmäßige Beschleunigung: Sp=S * P2
Gleichmäßige Verzögerung: Sp=S * P * (2−P)
Umfangs-x-Achse: Sp=S * cos(ω * P)
Umfang y-Achse: Sp=S * sin(ω * P)
(Ich habe die Ableitung der oben genannten vier Beziehungen nicht sorgfältig studiert. Ich habe viele meiner mathematischen Vorkenntnisse vergessen. Wenn Sie interessiert sind, können Sie „Was Sie über Animation wissen müssen“ studieren)
Wenn Sie bei derselben Animation die oben genannten unterschiedlichen Regeln anwenden, können Sie sehen, wie sich die Effekte der Animation mit unterschiedlichen Geschwindigkeiten ändern, und schließlich unser Ziel erreichen, mithilfe von Animationen die Bewegung von Objekten in der realen Welt zu simulieren.
Bei der Untersuchung der Beziehung zwischen diesen Offsets und dem Animationsprozess haben wir festgestellt, dass der Gesamtoffset S nur ein Parameter in dieser Beziehung ist. Wenn S entfernt wird, erhalten wir einen Wert, der nichts mit S zu tun hat bezieht sich nur auf den Animationsprozess:
Gleichmäßige Geschwindigkeit: ep = p
Gleichmäßige Beschleunigung: ep= P2
Gleichmäßige Verzögerung: ep= P * (2−P)
Umfang x-Achse: ep= cos(ω * P)
Umfang y-Achse: ep= sin(ω * P)
Die Verwendung einer Funktion zur Darstellung aller oben genannten Regeln lautet: ep = E(P), P∈[0,1], P stellt den Animationsprozess dar, ep stellt die Tendenz dar. Der Prozentsatz der abgeschlossenen Bewegung. Was hinzugefügt werden muss, ist, dass diese Beziehung auch eine Bedingung erfüllen muss: Wenn P=0, muss ep 0 sein; wenn P=1, muss ep 1 sein. Dies sollte leicht zu verstehen sein, da P=0 und P=1 sowie ep=0 und ep=1 jeweils den Start- und Endzustand der Animation darstellen .
Mit anderen Worten: Solange wir eine Funktion finden, die alle Bedingungen des vorherigen Absatzes erfüllt, wie z. B. die vorherigen, kann diese Funktion als Methode für uns verwendet werden, um die Geschwindigkeit der Animation zu steuern. Diese Funktion
ist die sogenannte Animationsoperator-Erleichterung. Als Animationsoperatoren können folgende Funktionsbilder verwendet werden:
有了这个规律,就赋予了动画效果控制无限的可能性,因为能满足前面那些条件的函数是无穷的。而这些看起来无穷尽的函数,我们能够轻松地通过贝塞尔曲线工具绘制出来,并且在css里面我们可以直接把这个工具的参数直接应用于transition跟animation里面。js里面也有bezier-easing 库可以使用这个工具的参数,然后应用到我们用js写的动画里面。比如:
<span style="color: #0000ff"><span style="color: #800000">script</span><span style="color: #0000ff">></span> <span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> box </span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5"> document.getElementById(</span><span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">box</span><span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">); </span><span style="color: #0000ff; background-color: #f5f5f5">function</span><span style="color: #000000; background-color: #f5f5f5"> start() { </span><span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> duration </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #000000; background-color: #f5f5f5">1000</span><span style="color: #000000; background-color: #f5f5f5">;</span><span style="color: #008000; background-color: #f5f5f5">//</span><span style="color: #008000; background-color: #f5f5f5">动画时长</span> <span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> s </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #000000; background-color: #f5f5f5">120</span><span style="color: #000000; background-color: #f5f5f5">;</span><span style="color: #008000; background-color: #f5f5f5">//</span><span style="color: #008000; background-color: #f5f5f5">总的偏移量</span> <span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> start_time </span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5"> Date.now(); </span><span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> easing </span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5"> BezierEasing(</span><span style="color: #000000; background-color: #f5f5f5">0.86</span><span style="color: #000000; background-color: #f5f5f5">, </span><span style="color: #000000; background-color: #f5f5f5">0</span><span style="color: #000000; background-color: #f5f5f5">, </span><span style="color: #000000; background-color: #f5f5f5">0.07</span><span style="color: #000000; background-color: #f5f5f5">, </span><span style="color: #000000; background-color: #f5f5f5">1</span><span style="color: #000000; background-color: #f5f5f5">); </span><span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> timer </span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5"> setInterval(</span><span style="color: #0000ff; background-color: #f5f5f5">function</span><span style="color: #000000; background-color: #f5f5f5">(){ </span><span style="color: #008000; background-color: #f5f5f5">//</span><span style="color: #008000; background-color: #f5f5f5">percent表示动画的进程</span> <span style="color: #0000ff; background-color: #f5f5f5">var</span><span style="color: #000000; background-color: #f5f5f5"> percent </span><span style="color: #000000; background-color: #f5f5f5">=</span><span style="color: #000000; background-color: #f5f5f5"> (Date.now() </span><span style="color: #000000; background-color: #f5f5f5">-</span><span style="color: #000000; background-color: #f5f5f5"> start_time) </span><span style="color: #000000; background-color: #f5f5f5">/</span><span style="color: #000000; background-color: #f5f5f5"> duration; </span><span style="color: #0000ff; background-color: #f5f5f5">if</span><span style="color: #000000; background-color: #f5f5f5">(percent </span><span style="color: #000000; background-color: #f5f5f5">>=</span> <span style="color: #000000; background-color: #f5f5f5">1.0</span><span style="color: #000000; background-color: #f5f5f5">) { percent </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #000000; background-color: #f5f5f5">1</span><span style="color: #000000; background-color: #f5f5f5">; clearInterval(timer); console.log(</span><span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">动画运行时间(ms): </span><span style="color: #000000; background-color: #f5f5f5">'</span> <span style="color: #000000; background-color: #f5f5f5">+</span><span style="color: #000000; background-color: #f5f5f5"> (</span><span style="color: #0000ff; background-color: #f5f5f5">new</span><span style="color: #000000; background-color: #f5f5f5"> Date().getTime() </span><span style="color: #000000; background-color: #f5f5f5">-</span><span style="color: #000000; background-color: #f5f5f5"> start_time)); } box.style.transform </span><span style="color: #000000; background-color: #f5f5f5">=</span> <span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">translateX(</span><span style="color: #000000; background-color: #f5f5f5">'</span> <span style="color: #000000; background-color: #f5f5f5">+</span><span style="color: #000000; background-color: #f5f5f5"> (Math.floor(s </span><span style="color: #000000; background-color: #f5f5f5">*</span><span style="color: #000000; background-color: #f5f5f5"> easing(percent))) </span><span style="color: #000000; background-color: #f5f5f5">+</span> <span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">px)</span><span style="color: #000000; background-color: #f5f5f5">'</span><span style="color: #000000; background-color: #f5f5f5">; },</span><span style="color: #000000; background-color: #f5f5f5">16</span><span style="color: #000000; background-color: #f5f5f5">); } </span><span style="color: #0000ff"></span><span style="color: #800000">script</span><span style="color: #0000ff">></span></span>
总之,有了ease跟贝塞尔曲线工具,要实现不同的动画速度控制效果,就变成一件特别容易的事情了。
最后,希望这篇文章能帮助到一些朋友更好理解动画的原理以及动画速度控制的正确方式。