本文由小聲團隊出品昂验,小聲團隊是一個專注于音頻&音樂技術(shù)的初創(chuàng)團隊店煞,深度使用Flutter構(gòu)建跨平臺應(yīng)用获讳,希望與大家一起共同探索Flutter在桌面端&移動端的可能性。
當(dāng)我們在進(jìn)行列表頁開發(fā)的時候愤诱,通常會使用Lazy Load 技術(shù) (在MDN中打開)來加載數(shù)據(jù)云头,關(guān)于Lazy Loading技術(shù)的細(xì)節(jié)本文不再贅述。
在我們有了Lazy Loading的背景知識之后淫半,我們一起來思考如果你現(xiàn)在面對一個超大型的Canvas(畫布)溃槐,上面可能布滿了上萬個元素,并且隨時在變動撮慨,那么你會怎么處理竿痰?
在大多數(shù)Canvas引擎中都沒有局部刷新的概念,我們只能重繪整個頁面砌溺,這也就意味著,如果我們一個Canvas里面有1萬個元素变隔,那么每一幀都需要渲染
這一萬個元素规伐,無論它是否變動。很顯然匣缘,這形成了非常低效的渲染性能猖闪。
在上面的圖片中,我們可以發(fā)現(xiàn)物理顯示設(shè)備的尺寸是遠(yuǎn)遠(yuǎn)低于Canvas本身的大小的肌厨,在應(yīng)用上這個時候會有滾動條來進(jìn)行垂直與橫向方向滾動來查看屏幕之外的內(nèi)容培慌,但是如果我們不做任何的優(yōu)化,那么屏幕之外的內(nèi)容也被計算與渲染了柑爸,僅僅只是沒有顯示而已吵护,對于上面的這樣的場景,我們的做法是對于可見范圍之外的內(nèi)容不進(jìn)行渲染表鳍,這個概念通常叫做Viewport(視區(qū))馅而。
對于Flutter本身而言,已經(jīng)提供了Widget - ViewPort 以及眾多基于ViewPort的子類來進(jìn)行不同場景的編排譬圣,Viewport接收一個List<Widget>,自動根據(jù)Widget的布局來控制是否build與渲染瓮恭。在Canvas場景中,我們則沒有辦法使用這些內(nèi)置的方案來實現(xiàn)Viewport,因為單個Canvas元素沒法作為一個Widget厘熟,因此我們需要自己來實現(xiàn)一套類似的方案屯蹦。
第一步,我們先構(gòu)建一個簡單的盒模型
class Box {
double x;
double y;
double width;
double height;
void render();
}
第二步绳姨,構(gòu)建一個簡單的Viewport類登澜,horizontalScrollController 為橫向滾動,verticalScrollController為縱向滾動就缆。
class Viewport extends Box {
List<Box> children;
ScrollController horizontalScrollController;
ScrollController verticalScrollController;
}
對滾動添加事件
initEvent(){
horizontalScrollController.addListener(render);
verticalScrollController.addListener(render);
}
render() {
var offsetX = horizontalScrollController.offset;
var offsetY = verticalScrollController.offset;
//這里我們簡單的用容器的可視區(qū)域來建立Viewport
var startX = offsetX - width;
var endX = offsetX + width;
var startY = offsetY - height;
var endY = offsetY + height;
// 將不可見元素直接過濾
children
.where((e) => e.x + e.width >= startX && e.x + e.width <= endX && e.y + e.height >= startY && e.y + e.height<= endY)
.forEach((e){
e.render();
});
}
自此帖渠,我們就已經(jīng)完成了一個最小的Canvas Lazy Render模型,其原理就是監(jiān)聽滾動事件竭宰,按照當(dāng)前的滾動偏移量計算可見區(qū)域的元素空郊,然后進(jìn)行過濾渲染份招。這樣,應(yīng)用在超大規(guī)模的Canvas中是有極大性能提升的技術(shù)方案狞甚。