微信小遊戲原生排行版的實現
微信小遊戲原生排行版的實現
在之前微信小遊戲子域踩坑記錄中就已經提到過包體過大的問題了,現在這個問題終於爆了,今天花了一天的時間使用原生介面重寫了之前的子域工程,也算是償還技術債務了。使用creator1.9.3打包出來的子域工程大小為755kb,而使用原生排行版只要佔用50kb不到的空間,包括所有美術資源。
基本流程
先來聊一聊開放資料域如何顯示:在小遊戲環境下,關係鏈資料只能在一個封閉的子域中獲取,子域Canvas無法直接顯示,只能被繪製到上屏Canvas上。上屏Canvas能向sharedCanvas傳送訊息,反之不行。
- 上屏Canvas傳送顯示指令
- sharedCanvas接收指令,並渲染相關關係鏈資料
- 上屏Canvas將sharedCanvas的內容繪製出來
顯示層級
Canvas中後繪製的會在上面,所以需要注意繪製的順序,否則會出現被遮擋的情況。另外由於image的載入是非同步的,所以我使用Promise對所有的image繪製進行封裝。將所有的渲染分成兩個部分:非同步的和同步的。
renderImages(){ if(!this.isVisible()){ return []; } let promises = []; promises.push(this.drawImage('friendContent', 0, 0, 640, 128)); let rankSrc; switch (this.info.rank) { case 1: rankSrc = 'first'; break; case 2: rankSrc = 'second'; break; case 3: rankSrc = 'third'; break; default: rankSrc = 'others'; break; } promises.push(this.drawImage(rankSrc, -10, -20)); promises.push(this.drawImage('friendIconOn', 376, 22)); promises.push(this.drawImage('starNum', 156, 80)); if(this.info.avatarUrl){ promises.push(this.drawImage(this.info.avatarUrl, 30, 17, 88, 88, true)); }else{ promises.push(this.drawImage('friendDefaultUserIcon', 30, 17, 88, 88)); } return promises; } renderTexts(){ if (!this.isVisible()) { return; } this.drawText(this.info.name, 156, 54, 24, '#A46E63', 'left'); this.drawText(this.info.starNum, 262, 95, 18, '#A46E63', 'left'); this.drawText(this.info.rank, 10, 10, 24, null, 'center'); }
在呼叫的時候,圖片都是在最下方的,文字由於需要顯示資訊,必然可以在所有圖片繪製完成後繪製。上面的程式碼是一個排行版item的繪製方法,我們會先呼叫圖片繪製方法,獲得所有的promise,並且真正繪製,最後呼叫同步繪製方法。
render(){ for(const item of this.items){ item.setPosition((this.canvas.width - 640) / 2, this.deltaY); } const allDrawPromises = []; for (const item of this.items) { const promises = item.renderImages(); for(let promise of promises){ allDrawPromises.push(promise); } } Promise.all(allDrawPromises).then((drawInfos) => { this.clear(); this.renderBg(); for (const info of drawInfos) { if (info.width) { this.ctx.drawImage(info.img, info.left, info.top, info.width, info.height); } else { this.ctx.drawImage(info.img, info.left, info.top); } } for (const item of this.items) { item.renderTexts(); } }).catch((err) => console.warn(err)); }
這樣我們就可以保證了顯示的層級
滾動實現
滾動的實現也很簡單,在主域Canvas顯示子域資訊的區域監聽TOUCH_MOVE事件,然後向子域傳送Scroll訊息,然後子域按照訊息繪製。這裡我們只談子域的實現。我們首先需要儲存一個全域性偏移資訊,然後再繪製的時候加上這個偏移,並且保證偏移的上下限。在接收到訊息後,我們修改這個偏移,並且重新繪製子域。
scroll(info){
if(this.deltaY - info.y >= this.maxY){
return;
}
if (this.deltaY - info.y - this.canvas.height <= this.minY){
return;
}
this.deltaY -= info.y;
this.deltaY = Math.max(this.minY, Math.min(this.maxY, this.deltaY));
this.render();
}
image不要重複建立
你需要一個image快取,而不是每次繪製都重新等待image的載入。我們的繪製會很頻繁,如果每次繪製都重新載入image會很卡!!!!而且會佔用額外的記憶體,所以僅在第一次繪製的時候載入image
僅繪製必要的部分
如果一個item不會顯示出來,那麼你不就不應該繪製它。這個可以通過你item的index(排名)和Canvas的高度(僅考慮上下滑動)以及偏移量來判定
setPosition(left, top){
top = top + (ITEM_HEIGHT + ITEM_SPACINGY) * (this.info.rank - 1);
this.left = left;
this.top = top;
}
isVisible(){
if (this.top >= this.canvas.height || this.top <= -ITEM_HEIGHT) {
return false;
}else{
return true;
}
}