[英]Performance improvements to SVG path rendering in Canvas
Tl; DR:使用下面的代碼,我的渲染時間非常慢,任何原因或可能如何改善性能的建議都將受到深深的贊賞。
我正在開發需要支持多個渲染后端(例如SVG
和Canvas
)的應用程序,但是我遇到了一些嚴重的性能問題,當我將其渲染到Canvas時沒有想到。
正確,因此我的代碼得以構建,因此每個后端都實現了類似Canvas的繪圖API,因此無論使用哪種后端,渲染語法都保持不變。 我的代碼正在渲染“字形”,這些字形被定義為SVG路徑(最初來自SVG字體文件)。 問題在於畫布的渲染出奇地慢-幾乎與具有大量 DOM交互的SVG一樣慢。
我希望有一個渲染時間,使動畫幀速率至少為30 FPS,但現在,使用Canvas,單個幀大約需要50-70ms(Chrome),而使用SVG,單個幀大約需要80-90ms,這最好導致〜20 FPS。 我現在正在渲染30個字形,平均數量約為24.5個繪制命令。
我的問題是:是否有一種更有效的方法來進行這種渲染或以任何方式獲得更好的性能,因為這種方法的低效率讓我感到震驚(即使我緩存了字形!)。 Glyph
是一個對象,在初始化時會將SVG路徑字符串解碼為(我認為是)更快的表示法,從而使路徑成為數組數組。 例如:
[['M', 201, 203.5551],['s', 15.2, 13.254, 15.3, 18.5, 22.3, 50.118], ...]
我的CanvasRenderingContext2D#renderGlyph
方法定義為類似的形式,其中glyph.path
對象(數組)的定義如上:
canvas.renderGlyph = function canvasRenderGlyph(name, x, y, nocache) {
if (!(name instanceof Glyph) && !font.glyphs[name]) {
return console.log('Unsupported Glyph: ' + name, 'warn');
}
x = x * scale;
y = y * scale;
var glyph, path, c, startx, starty, px, py, controlpx, controlpy;
if (typeof name === 'string' && name in glyphCache && !nocache) {
glyph = glyphCache[name];
} else {
glyph = (name instanceof Glyph) ? name : new Glyph(font.glyphs[name]);
glyph.scale(scale * font.scale.x, scale * font.scale.y);
if (typeof name === 'string') {
glyphCache[name] = glyph;
}
}
path = glyph.path;
startx = x;
starty = y;
px = 0;
py = 0;
this.beginPath();
for (var i = 0, length = path.length; i < length; i++) {
c = path[i];
switch (c[0]) {
case 'M':
px = c[1];
py = c[2];
this.moveTo(startx + px, starty + py);
break;
case 'l':
px += c[1];
py += c[2];
this.lineTo(startx + px, starty + py);
break;
case 'h':
px += c[1];
this.lineTo(startx + px, starty + py);
break;
case 'v':
py += c[1];
this.lineTo(startx + px, starty + py);
break;
case 'q':
controlpx = px + c[1];
controlpy = py + c[2];
px += c[3];
py += c[4];
this.quadraticCurveTo(
startx + controlpx, starty + controlpy, startx + px, starty + py);
break;
case 't':
controlpx = px + (px - controlpx);
controlpy = py + (py - controlpy);
px += c[1];
py += c[2];
this.quadraticCurveTo(
startx + controlpx, starty + controlpy, startx + px, starty + py);
break;
case 'c':
controlpx = px + c[3];
controlpy = py + c[4];
this.bezierCurveTo(
startx + px + c[1], starty + py + c[2], startx + controlpx, starty + controlpy, startx + px + c[5], starty + py + c[6]);
px += c[5];
py += c[6];
break;
case 's':
this.bezierCurveTo(
startx + controlpx, starty + controlpy, startx + px + c[1], starty + py + c[2], startx + px + c[3], starty + py + c[4]);
px += c[3];
py += c[4];
controlpx = px + c[1];
controlpy = py + c[2];
break;
case 'z':
this.closePath();
break;
default:
if (c[0].match(/[a-z]/i)) {
console.log('Unsupported path command: ' + cname, name, 'warn');
}
break;
}
}
this.fillStyle = self.settings.fillcolor;
this.fill();
};
肯定有一些事情要解決。 性能不應該這么差,無法繪制約24個命令復雜度的字形。
例如, Fabric.js能夠以30fps的速度使用數千個命令來渲染路徑。 在Fabric中,我使用類似的方法將SVG路徑數據解析為命令數組,然后調用相應的上下文方法。
還考慮一下Fabric可以識別更多命令 (您的示例缺少大多數絕對命令 -Q,C,S等)。
在此動畫示例中 ,您可以看到不錯的性能渲染〜4000,〜5000路徑。 只有當它上升到約10000時,您才開始看到速度下降(FPS計數器此刻處於崩潰狀態,因此我只在談論可感知的性能)。
可能會影響您性能的其他因素-畫布大小,頁面上是否可能存在其他元素,有關動畫循環的信息。 另外,您看到哪種硬件/平台性能不佳?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.