首页 文章

Canvas中SVG路径渲染的性能改进

提问于
浏览
0

Tl; DR:我的渲染时间非常慢,使用下面的代码 - 任何想法为什么会这样或者如何提高性能的建议将深受赞赏 .

我正在开发一个需要支持多个渲染后端的应用程序(例如 SVGCanvas ),但是在渲染到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 回答

  • 1

    关于这一点肯定有些不对劲 . 性能不应该太差,渲染~24命令复杂度的字形 .

    例如,Fabric.js能够以30fps的速度渲染具有 thousands 命令的路径 . 在Fabric中,我使用类似的方法将SVG路径数据解析为命令数组,然后调用相应的上下文方法 .

    还要考虑Fabric recognizes more commands(您的示例缺少大多数绝对的 - Q,C,S等)

    在这个animation example中,你可以看到一个不错的性能渲染~4000,~5000路径 . 只有当它达到~10000时,你才开始看到减速(此时FPS计数器被破坏,所以我只是谈论感知性能) .

    其他可能影响你性能的东西 - 画布大小,页面上可能存在其他元素,有关动画循环的东西 . 另外,您认为哪个硬件/平台性能不佳?

相关问题