Tl;DR:我的渲染时间出奇地慢,使用下面的代码 - 任何想法为什么会这样或关于如何提高性能的建议将不胜感激。
我正在开发一个需要支持多个渲染后端(例如SVG
和Canvas
)的应用程序,但是我遇到了一些在渲染到 Canvas 时没有预料到的严重性能问题。
对,所以我的代码是这样构建的,这样每个后端都实现了一个类似 Canvas 的绘图 API,这样无论使用什么后端,渲染语法都保持不变。我的代码正在渲染定义为 SVG 路径(最初来自 SVG 字体文件)的“字形”。问题是画布渲染速度非常慢 - 几乎与具有大量DOM 交互的 SVG 一样慢。
我希望渲染时间允许动画帧速率至少为 30 FPS,但现在,使用 Canvas 时,单个帧大约需要 50-70 毫秒(Chrome),使用 SVG 时需要 80-90 毫秒,这充其量会导致〜20 FPS。我现在正在渲染 30 个字形,平均数量约为 24.5 个绘图命令。
我的问题是:有没有更有效的方法来进行这种渲染或任何方法来获得更好的性能,因为我对这种方法的低效率感到震惊(即使我缓存了字形!)。AGlyph
是一个对象,它在初始化时将 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();
};