ホームページ > 記事 > ウェブフロントエンド > なぜ速度と時間の関係を利用してアニメーションを実装できないのでしょうか?
最近いくつかのページアニメーションエフェクトを作成したため、これまであまり経験がありませんでしたが、今回のプロセス中にいくつかの問題に遭遇しました。また、アニメーションに関するブログ「アニメーションについて、知っておくべきこと」を読みました。 Ten Years of Traces の「 a long time ago. 」を読んだので、この記事のようなアニメーションの基本原理に関するいくつかの疑問について考えてみました。この質問は単純な場合もあれば、非常に単純な場合もあります。たとえば、この記事には、さらに詳細な方法が記載されています。
等速直線運動を例として、物体の客観的な動きでは、速度と時間の曲線、または変位と時間の曲線を使用して物体の動きを同時に記述することができます:
それが関係であるかどうか速度と時間または変位との関係 時間に対する対象物の直線運動を記述する場合、対象物の動きは常に客観的な時間軸に沿って変化するため、人間が変えることはできません。時間軸上のどの点においても、物体の状態は常に同じです。それに対応する特定の速度と変位が存在します。
Webアニメーションでも動きとして表現されますが、対象物の運動規則を使って表現することはできません。その主な理由は、アニメーションの本質は動きではなく、タイマーに基づいた要素の状態の瞬間的な変化であるためだと思います。単純な要素の水平均一オフセットのアニメーション効果を例に挙げると、このアニメーションを実現するには、タイマーを使用して要素の x 軸オフセットを一定の時間間隔でリセットするだけで済みます。これは画像で説明できます。図中の
T1~t6 は、タイマー コールバック関数が実行される時間を表します。この効果では、要素のオフセット位置はタイマーが実行されるたびに変化しますが、要素のオフセット位置は隣接する 2 つの実行時間の間では変化しません。表示されるアニメーションはタイマー間隔が短すぎるためであり、タイマー間隔を十分に長くすると、間隔中の要素のステータスを視覚的に認識できなくなります。
アニメーションは動きではないため、アニメーションのプロセスを理解しようとするとき、動きの法則に基づいて考えることはできません。例えばアニメーションが止まった瞬間の状態をどのように理解すればよいのでしょうか?先ほどのアニメーション効果を例にとると、タイマーがクリアされるとアニメーションは瞬時に停止します。その要素については、アニメーション速度が突然0になります。これを対象物の動きに例えると、合計と考えることができます。当然のことながら、要素のアニメーションは停止する前に減速する必要があります。このように考えると、要素のアニメーションが停止するときに突然停止する原理を理解できなくなります。しかし、この問題をアニメーションの本質から考えると、アニメーションの処理中に要素の状態を変更する唯一の要素がタイマーであるため、タイマーが機能しない場合は外部の要素が存在しないため、容易に理解できます。力は要素の状態を変えるために使われますが、どうやってまだ動くことができるのでしょうか?
アニメーションは動きではありませんが、よりスムーズで目的の世界に近いアニメーション効果を作成するために、アニメーションの速度を適切に制御する方法を見つけたいと考えています。速度について話すとき、速度を思い浮かべやすいのは、対象物の移動において、速度は動きの速度を説明するために使用される要素だからです。また、速度ルールを使用してアニメーションの速度を制御することは、理解して実装するのが簡単であるように思えます。前の例をより具体的にすると、要素が 1 秒以内に一定の速度で右に 120 ピクセル移動するアニメーション効果を実現したい場合は、タイマーを使用して要素を制御し、固定量を右に移動するだけで済みます。ここでは、タイマーが実行されるたびに要素に追加されるオフセットが、アニメーションの制御に使用される速度になります。タイマー間隔として 16 ミリ秒を使用する場合、このアニメーションの速度は次のように計算できます: 120px / (1000ms / 16ms) (ほぼ 2px に等しい)、つまり、タイマーが要素を右にオフセットしている限り実行されるたびに、2px で必要な効果を実現できます。簡単なコード実装は次のとおりです:
rrree
在浏览器中运行以上代码,动画效果肯定是跟预期一致的,而且动画的实际执行时间也与规定的时长相差很小:
至于为什么不完全等于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>
実際の実行結果は以下の通りです。
次に、その方法をまとめてみましょう。まず、コードでこのメソッドを使用していることがわかります
アニメーションプロセスの概念を導入し、アニメーションプロセスを通じてアニメーションの完了を制御します:
パーセントアニメーション処理は目標タイムラインに基づいているため、指定時間内にアニメーションを完了することが保証され、速度制御アニメーションを使用した場合にアニメーションの実行時間が延長されるという問題がなくなりました。 (もちろん、アニメーションの実行中に非常に時間のかかるタスクを追加すると、指定された時間内にアニメーションを完了できなくなります)。
その後、アニメーションのオフセットを扱うときは、現在の実行時間でのオフセットを取得するために、合計オフセット * アニメーション プロセスを追加するだけで済みます。
最後に、アニメーションの進行状況が 1 になると、アニメーションは終了し、要素はアニメーションで指定された合計オフセットに設定されます。
一般に、このメソッドは変位と時間の関係をオフセットとアニメーション処理の関係に変換します。アニメーション プロセスを通じて、アニメーションの継続時間とアニメーション オフセットの完了を同時に制御できます。
さらに重要なのは、オフセットとアニメーション プロセスの関係は、客観的な運動法則を通じて推定できることです。
たとえば、上記の例では、一定速度のアニメーションであるため、そのルールは次のようになります:
アニメーションプロセス p = t / T; (t = Date.now() – start_time ; T = 継続時間)
オフセット Sp = S * p; (S は合計オフセット、Sp は現在のオフセット)
等加速アニメーション、等減速アニメーション、円運動アニメーションなど他のアニメーションでも同様のルールを取得できます。すべてのアニメーション効果のアニメーション プロセスの計算方法は同じです。唯一の違いは、オフセットとアニメーション プロセスの関係です。
等加速度: Sp=S * P2
等減速度:Sp=S * P * (2−P)円周x軸: Sp=S * cos(ω * P)
円周y軸: Sp=S * sin(ω * P)
(私は上記の 4 つの関係の導出を注意深く勉強していません。以前の数学的知識の多くを忘れています。興味がある場合は、「アニメーションについて知っておくべきこと」を勉強してください)
同じアニメーションに上記の異なるルールを適用すると、アニメーションの変化効果が異なる速度で表示され、最終的にアニメーションを使用して現実世界のオブジェクトの動きをシミュレートするという目標を達成できます。
これらのオフセットとアニメーション プロセスの関係を調査すると、合計オフセット S はこの関係における単なるパラメーターであることがわかりました。S が削除されると、S とはまったく無関係で、アニメーション プロセスに関連する式だけが得られます。
均一速度: e
p= p 等加速度: e
p= P2 等減速度: e
p= P * (2−P) 円周x軸: e
p= cos(ω * P) 円周y軸: e
p= sin(ω * P) 関数を使用して上記のルールをすべて表すと、次のようになります: e
p= E(P), P∈[0,1]、P はアニメーション プロセスを表し、ep はオフセットの完了パーセンテージを表します。追加する必要があるのは、この関係は条件も満たさなければならないということです。P=0 の場合、ep は 0 でなければならず、P=1 の場合、ep は 1 でなければなりません。 P=0 と P=1、ep=0 と ep=1 はそれぞれアニメーションの開始状態と終了状態を表すため、これは理解しやすいはずです。 言い換えれば、前の段落の条件をすべて満たす関数が見つかった限り、この関数はアニメーションの速度を制御する方法として使用できます。この機能
いわゆるアニメーションオペレータの楽さです。次の関数イメージはアニメーション オペレーターとして使用できます:
有了这个规律,就赋予了动画效果控制无限的可能性,因为能满足前面那些条件的函数是无穷的。而这些看起来无穷尽的函数,我们能够轻松地通过贝塞尔曲线工具绘制出来,并且在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跟贝塞尔曲线工具,要实现不同的动画速度控制效果,就变成一件特别容易的事情了。
最后,希望这篇文章能帮助到一些朋友更好理解动画的原理以及动画速度控制的正确方式。