この記事では、ミニ プログラムのアニメーション最適化の実践方法と、ミニ プログラムのショッピング カートのアニメーションを最適化する方法について説明します。皆さんのお役に立てれば幸いです。
会社のミニプログラムがクリックして購入を追加すると、放物線状のアニメーションが描画されます。 . この放物線アニメーションは計算されています ベジェ曲線上の各点の座標を js で巡回して、点のスタイルを動的に設定してアニメーションを実現します。ただし、これによりフレームのスタックやドロップの問題が発生します。
this.goodBoxTimer = setInterval(() => { index-- this.setData({ 'movingBallInfo.posX': linePos[index][0], 'movingBallInfo.posY': linePos[index][1], }) if (index < 1) { this.resetGoodBoxStatus() } }, 30)
前提知識: イベント ループ、タスク、マイクロ タスク、UI レンダリング
JavaScript はシングルスレッド言語であるため、すべてのタスクをキューに入れる必要があります。タスクには同期タスク(synchronous)と非同期タスク(asynchronous)の2種類があります。同期タスクとは、メイン スレッドで実行するためにキューに入れられたタスクを指します。次のタスクは、前のタスクが実行された後にのみ実行できます。非同期タスクとは、メイン スレッドには入らず、「タスク キュー」に入るタスクを指します。タスク、 「タスクキュー」が非同期タスクを実行できることをメインスレッドに通知した場合にのみ、タスクは実行のためにメインスレッドに入ります。
非同期タスクはマクロタスク(Task)とマイクロタスク(microTask)に分けられ、同様にタスクキューもマクロタスクキューとマイクロタスクキューに分けられます。
イベント ループの大まかな手順:
すべての同期タスクはメイン スレッドで実行され、実行コンテキスト スタックを形成します。
非同期タスクに実行結果がある限り、イベントはタスク キューに配置されます。
実行スタック内のマクロ タスクが実行された後、エンジンはまずマイクロ タスクを読み取り、実行スタックにプッシュします。実行が完了したら、次のマイクロタスクの読み取りを続けます。実行中に新しいマイクロタスクが生成されると、このマイクロタスクはマイクロタスク キューにプッシュされます。メインスレッドがマイクロタスクキュー内のすべてのタスクの実行を終了すると、マクロタスクキューを読み取り、実行スタックにプッシュします。
メインスレッドは上記の 3 番目のステップを繰り返し続けます。
一般的なマクロ タスク:
一般的なマイクロタスク:
そして、イベント ループと UI レンダリングの間にはどのような関係があるのでしょうか?実際、マイクロタスク キュー内のすべてのマイクロタスクが実行された後、ブラウザはレンダリングの更新を実行するかどうかを決定します。
// demo1 // 渲染发生在微任务之后 const con = document.getElementById('con'); con.onclick = function () { Promise.resolve().then(function Promise1 () { con.textContext = 0; }) };
// demo2 // 两次EventLoop中间没有渲染 const con = document.getElementById('con'); con.onclick = function () { setTimeout(function setTimeout1() { con.textContent = 0; Promise.resolve().then(function Promise1 () { console.log('Promise1') }) }, 0) setTimeout(function setTimeout2() { con.textContent = 1; Promise.resolve().then(function Promise2 () { console.log('Promise2') }) }, 0) };
通常の状況におけるブラウザのフレーム レートは 60fps であることがわかっています。つまり、1 フレームの時間は約16.66ミリ秒。 Dom が 1 つのフレーム内で 2 回変更された場合、ブラウザは最後に変更された値のみを使用してレンダリングします。
// demo3 // 两次eventloop中有渲染 const con = document.getElementById('con'); con.onclick = function () { setTimeout(function setTimeout1() { con.textContent = 0; }, 0); setTimeout(function setTimeout2() { con.textContent = 1; }, 16.7); };
setInterval は使用しないようにしてください
上記のことから、setInterval はマクロ タスク、setInterval each 定義された時間間隔でコールバック関数がマクロ タスク キューにプッシュされ、メイン スレッドがマクロ タスク キュー内の setInterval コールバック関数を読み取って実行します。ただし、メインスレッドで長時間タスクが実行されている場合、メインスレッドのタスクが実行されるまで読み取りがブロックされ、読み取りは続行されませんが、マクロタスクキューにコールバック関数を追加する setInterval 操作は停止しません。この場合、次のような問題が発生します。 関数の実行間の時間間隔が、定義した時間間隔よりも大幅に長くなります。
次は例です。各 setInterval コールバックには大量の計算が必要であり、メイン スレッドがブロックされます。
// demo4 const btn = document.getElementById('btn') btn.addEventListener('click', setIntervalFn) let sum = 0 function setIntervalFn() { let last let countIdx = 0 const timer = setInterval(function timeFn() { countIdx++ const newTime = new Date().getTime() const gap = newTime - last last = newTime console.log('setInterval', gap, countIdx) if (countIdx > 5) clearInterval(timer) // 10000000 // 100000 for (let i = 0; i < 100000; i++) { sum+= i } }, 100) last = new Date().getTime() }
setInterval の欠点:
したがって、setInterval の代わりに setTimeout を使用してみてください。
requestAnimationFrame を使用してください。
js を使用してアニメーションを描画する場合, setTimeout ではなく、公式に推奨されている requestAnimationFrame を引き続き使用してください。
window.requestAnimationFrame()
アニメーションを実行したいことをブラウザーに伝え、次の再描画の前に指定されたコールバック関数を呼び出してアニメーションを更新するようにブラウザーに依頼します
由上面的例子可知,两个宏任务之间不一定会触发浏览器渲染,这个由浏览器自己决定,并且浏览器的帧率并会一直是60fps,有时可能会下降到30fps,而setTimeout的回调时间是写死的,就有可能导致修改了多次Dom,而只触发了一次ui更新,造成掉帧。
// demo5 const con = document.getElementById('con'); let i = 0; function rAF(){ requestAnimationFrame(function aaaa() { con.textContent = i; Promise.resolve().then(function bbbb(){ if(i < 5) {rAF(); i++;} }); }); } con.onclick = function () { rAF(); };
可以看到渲染了5次(五条竖直虚线)
小程序上的动画优化
小程序是双线程架构
好处是:ui渲染和js主线程是分开的,我们知道在浏览器中这两者是互斥的,所以当主线程有阻塞时,页面交互就会失去响应,而小程序中不会出现这样的情况。
坏处是:逻辑层、渲染层有通信延时,大量的通信也会造成性能瓶颈。
小程序提供了wxs用来处理渲染层的逻辑。
购物车抛物线动画优化
所以我们不应该用setInterval去执行动画,我们修改成,当点击加购时,把点击坐标与目标坐标传入wxs
,然后计算运行轨迹点的坐标计算,接着用requestAnimationFrame
执行动画帧
// wxs function executeCartAnimation () { curCoordIdx = coordArr.length - 1 ins.requestAnimationFrame(setStyleByFrame) } function setStyleByFrame() { if (curCoordIdx >= 0) { ins.selectComponent('.cart-animation').setStyle({ display: 'block', left: coordArr[curCoordIdx].x + 'px', top: coordArr[curCoordIdx].y + 'px' }) curCoordIdx -= 1 ins.requestAnimationFrame(setStyleByFrame) } else { ins.selectComponent('.cart-animation').setStyle({ display: 'none' }) } }
在真机上效果非常明显,低端安卓机上的动画也非常丝滑。但是录屏效果不好,这里就不放了。
【相关学习推荐:小程序开发教程】
以上が実践を記録し、ミニ プログラムのショッピング カート アニメーションを最適化する方法を確認します。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。