轉(zhuǎn)自博客原文連接:https://tong-h.github.io/2019/03/18/canvas-3/#more
看了很多大佬的文章萨螺,自己也對項目做了一些優(yōu)化镰绎,其實有很多地方平常碼代碼的時候稍微注意一下就能節(jié)約很多性能開銷
1.離屏渲染
在離屏canvas上預(yù)渲染相似的圖形或重復(fù)的對象,通俗的解釋是將離屏canvas當(dāng)成預(yù)渲染唁桩,在離屏canvas上繪制好一整塊圖形巴碗,繪制好后在放到視圖canvas中,適合每一幀畫圖運算復(fù)雜的圖形
比如你想把一張圖片放到canvas上扣癣,使用drawImage()方法惰帽,有三種寫法
// 將image放到目標(biāo)canvas指定位置
void ctx.drawImage(image, dx, dy);
// 將image放到目標(biāo)canvas指定位置,指定寬高渲染
void ctx.drawImage(image, dx, dy, dWidth, dHeight);
// 將image裁剪之后放到目標(biāo)canvas指定位置搏色,指定寬高渲染
void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
第一種只是把圖片原樣放到canvas里善茎,第二章指定寬高就意味著放大或者縮小圖片后再放進(jìn)去,帶三種是裁剪后再放大或者縮小放到canvas中频轿,這三種寫法操作依次增加垂涯,性能開銷也是依次提高
而離屏渲染就可以讓我們先把圖片裁剪成想要的尺寸內(nèi)容保存起來烁焙,繪制的時候就可以使用第一種寫法簡單的把圖片放進(jìn)去就完了
// 在離屏 canvas 上繪制
var offscreencanvas = document.createElement('canvas');
// 寬高賦值為想要的圖片尺寸
offscreencanvas.width = dWidth;
offscreencanvas.height = dHeight;
// 裁剪
offscreencanvas.getContext('2d').drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
// 在視圖canvas中繪制
viewcontext.drawImage(canvas, x, y);
2.分層畫布
多個相互重疊的canvas根據(jù)變化程度分開渲染,越復(fù)雜的場景越適合
比如一個簡單的游戲場景耕赘,游戲背景始終不變或者變化次數(shù)較少但是人物游戲的主體是一直在根據(jù)玩家的指揮不停的改變骄蝇,
場景:最近寫的一個畫板,比如這樣一個簡單的畫圓操骡,可以看到有原來的繪畫的痕跡九火,那就要 渲染沒畫圓之前的畫布 -> 再畫圓,鼠標(biāo)移動的時候不斷的執(zhí)行這個渲染過程
這個時候就可以使用分層畫布册招,畫畫在一個上層canvas上岔激,原來繪畫痕跡在下層canvas上,那么畫圓的過程就是清空 -> 畫圓是掰,畫好之后再放到下層canvas上虑鼎,這樣就不需要去渲染之前的畫布,就能節(jié)約性能開銷
3.一次性繪制
繪制操作的性能開銷較高键痛,可以創(chuàng)建一個包含所有線條的路徑炫彩,然后通過單個繪制路徑調(diào)用進(jìn)行繪制
從這樣
for (var i = 0; i < points.length - 1; i++) {
var p1 = points[i];
var p2 = points[i+1];
context.beginPath();
context.moveTo(p1.x, p1.y);
context.lineTo(p2.x, p2.y);
context.stroke();
}
變成這樣,在繪制復(fù)雜路徑時絮短,最好將所有點都放入路徑中江兢,而不是分別呈現(xiàn)各個片段
context.beginPath();
for (var i = 0; i < points.length - 1; i++) {
var p1 = points[i];
var p2 = points[i+1];
context.moveTo(p1.x, p1.y);
context.lineTo(p2.x, p2.y);
}
context.stroke();
4.使用requestAnimationFrame執(zhí)行動畫
canvas動畫的本質(zhì)是不斷地擦除和重繪,再結(jié)合一些時間控制的方法達(dá)到動畫的目的
顯示器刷新頻率是60Hz丁频,最平滑動畫的最佳循環(huán)間隔是1000ms/60杉允,約等于16.6ms
而requestAnimationFrame就是根據(jù)顯示器刷新頻率來的,這是瀏覽器專門為動畫提供的API席里,在運行時瀏覽器會自動優(yōu)化方法的調(diào)用夺颤,節(jié)省系統(tǒng)資源,提高系統(tǒng)性能胁勺,如果頁面不是激活狀態(tài)下的話世澜,requestAnimationFrame() 會被暫停調(diào)用以提升性能和電池壽命
詳細(xì)看這兒
5.清空畫布
三種方法性能,性能依次提高
context.fillRect()
context.clearRect()
canvas.width = canvas.width; // 一種畫布專用的技巧
6.減少調(diào)用canvas的api
比如像背景可以使用css屬性設(shè)置或者img標(biāo)簽加一些定位什么的
畫布的縮放可以使用CSS transforms署穗,不要將小畫布放大寥裂,而是去將大畫布縮小
var scaleX = canvas.width / window.innerWidth;
var scaleY = canvas.height / window.innerHeight;
var scaleToFit = Math.min(scaleX, scaleY);
var scaleToCover = Math.max(scaleX, scaleY);
stage.style.transformOrigin = '0 0'; //scale from top left
stage.style.transform = 'scale(' + scaleToFit + ')';
其他注意點
- 盡可能使用計算代替canvas渲染,通常情況下案疲,渲染比計算的開銷大很多(3~4 個量級)
- 減少改變 context 的狀態(tài)以及不要賦一些亂七八糟類型的值封恰,比如人家要一個number你要給一個string,瀏覽器會用一些額外時間來處理這些非法輸入褐啡,可能會造成三四倍的時間開銷
- 避免使用浮點數(shù)坐標(biāo)诺舔,使用非整數(shù)的坐標(biāo)繪制內(nèi)容,系統(tǒng)會自動使用抗鋸齒功能,嘗試對線條進(jìn)行平滑處理低飒,這又是一種性能消耗许昨。可以調(diào)用 Math.round 四舍五入取整
- 減少使用 shadowBlur 效果褥赊,和很多圖像環(huán)境渲染一樣糕档,陰影渲染的性能開銷通常比較高
參考文章
https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas
https://www.html5rocks.com/zh/tutorials/canvas/performance/
http://taobaofed.org/blog/2016/02/22/canvas-performance/