Tl; DR:我的渲染时间非常慢,使用下面的代码 - 任何想法为什么会这样或者如何提高性能的建议将深受赞赏 .
我正在开发一个需要支持多个渲染后端的应用程序(例如 SVG
和 Canvas
),但是在渲染到Canvas时我会期待 .
是的,因此构建了我的代码,以便每个后端实现类似Canvas的绘图API,因此无论使用什么后端,渲染语法都保持不变 . 我的代码渲染"Glyphs",它们被定义为SVG路径(最初来自SVG字体文件) . 问题是画布渲染速度非常慢 - 几乎和SVG一样慢,后者有很多DOM交互 .
我希望有一个渲染时间,允许动画帧速率至少为30 FPS,但是现在,单帧需要大约50-70ms(Chrome)和Canvas,80-90ms和SVG最好会导致~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();
};
1 回答
关于这一点肯定有些不对劲 . 性能不应该太差,渲染~24命令复杂度的字形 .
例如,Fabric.js能够以30fps的速度渲染具有 thousands 命令的路径 . 在Fabric中,我使用类似的方法将SVG路径数据解析为命令数组,然后调用相应的上下文方法 .
还要考虑Fabric recognizes more commands(您的示例缺少大多数绝对的 - Q,C,S等)
在这个animation example中,你可以看到一个不错的性能渲染~4000,~5000路径 . 只有当它达到~10000时,你才开始看到减速(此时FPS计数器被破坏,所以我只是谈论感知性能) .
其他可能影响你性能的东西 - 画布大小,页面上可能存在其他元素,有关动画循环的东西 . 另外,您认为哪个硬件/平台性能不佳?