이 기사에서는 미니 프로그램 애니메이션 최적화 실습을 공유하고 미니 프로그램 장바구니 애니메이션을 최적화하는 방법을 알아보겠습니다. 모두에게 도움이 되기를 바랍니다.
회사 미니 프로그램에서 구매를 추가하기 위해 클릭하면 포물선 애니메이션이 그려집니다. 이 포물선 애니메이션은 베지어 곡선의 각 점의 계산된 좌표입니다. 그런 다음 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는 단일 스레드 언어이므로 모든 작업이 대기 중입니다. 작업에는 두 가지 유형이 있습니다. 하나는 동기 작업(동기)이고 다른 하나는 비동기 작업(비동기)입니다. 동기 작업은 메인 스레드에서 실행을 위해 대기 중인 작업을 의미하며, 이전 작업이 실행된 후에만 다음 작업을 실행할 수 있습니다. "작업 대기열"이 비동기 작업이 실행될 수 있음을 메인 스레드에 알릴 때만 작업이 실행을 위해 메인 스레드에 들어갑니다.
비동기 작업은 매크로 작업(Task)과 마이크로 작업(Micro Task)으로 구분됩니다. 마찬가지로 작업 대기열도 매크로 작업 대기열과 마이크로 작업 대기열로 구분됩니다.
이벤트 루프의 대략적인 단계:
모든 동기화 작업은 메인 스레드에서 실행되어 실행 컨텍스트 스택을 형성합니다.
비동기 작업의 실행 결과가 있는 한 이벤트는 작업 대기열에 배치됩니다.
실행 스택의 매크로 작업이 실행된 후 엔진은 먼저 마이크로 작업을 읽고 이를 실행 스택에 푸시합니다. 실행이 완료된 후 다음 마이크로태스크를 계속 읽으세요. 실행 중에 새로운 마이크로태스크가 생성되면 이 마이크로태스크는 마이크로태스크 대기열로 푸시됩니다. 메인 스레드가 마이크로태스크 대기열의 모든 작업 실행을 마치면 매크로태스크 대기열을 읽고 이를 실행 스택에 푸시합니다.
메인 스레드는 위의 세 번째 단계를 계속 반복합니다.
공통 매크로 작업:
공통 마이크로 작업:
이벤트 루프와 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, 즉 한 프레임의 시간이 약 16.66ms라는 것을 알고 있습니다. Dom이 한 프레임에서 두 번 수정되면 브라우저는 마지막으로 수정된 값만 렌더링에 사용합니다.
// 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은 정의된 시간 간격마다 매크로 작업 대기열에 콜백 함수를 푸시합니다. 메인 스레드는 매크로를 읽습니다. 작업 대기열의 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!