toSVG method not working on extended class with Fabric.js

我与影子孤独终老i 提交于 2020-01-13 05:54:16

问题


I'm working on a project with Fabric.js.

Now I need to create a custom class and to export it to SVG.

I'm using Fabric.js tutorial to begin:

http://www.sitepoint.com/fabric-js-advanced/

Here is the javascript code:

var LabeledRect = fabric.util.createClass(fabric.Rect, {
  type: 'labeledRect',
  initialize: function(options) {
    options || (options = { });
    this.callSuper('initialize', options);
    this.set('label', options.label || '');
  },
  toObject: function() {
    return fabric.util.object.extend(this.callSuper('toObject'), {
      label: this.get('label')
    });
  },
  _render: function(ctx) {
    this.callSuper('_render', ctx);
    ctx.font = '20px Helvetica';
    ctx.fillStyle = '#333';
    ctx.fillText(this.label, -this.width/2, -this.height/2 + 20);
  }
});

var canvas = new fabric.Canvas('container');

var labeledRect = new LabeledRect({
  width: 100,
  height: 50,
  left: 100,
  top: 100,
  label: 'test',
  fill: '#faa'
});
canvas.add(labeledRect);
document.getElementById('export-btn').onclick = function() {
canvas.deactivateAll().renderAll(); 
window.open('data:image/svg+xml;utf8,' + encodeURIComponent(canvas.toSVG()));

};

Here the HTML:

<canvas id="container" width="780" height="500"></canvas>
<a href="#" id="export-btn">Export SVG</a>

Here is my jsfiddle..

http://jsfiddle.net/QPDy5/

What I'm doing wrong?

Thanks in advance.


回答1:


Every "class" in Fabric (rectangle, circle, image, path, etc.) knows how to output its SVG markup. The method responsible for it is toSVG. You can see toSVG of fabric.Rect, for example, or fabric.Text one.

Since you created subclass of fabric.Rect here and didn't specify toSVG, toSVG of the next object in prototype chain is used. The next "class" in the inheritance chain happens to be fabric.Rect, so you're seeing a result of fabric.Rect.prototype.toSVG.

The solution is simple: specify toSVG in your subclass. Theoretically, you would need to combine the code from fabric.Rect#toSVG and fabric.Text#toSVG, but to avoid repetition and keep things maintainable, we can utilize a bit of a hack:

toSVG: function() {
  var label = new fabric.Text(this.label, {
      originX: 'left',
      originY: 'top',
      left: this.left - this.width / 2,
      top: this.top - this.height / 2,
      fontSize: 20,
      fill: '#333',
      fontFamily: 'Helvetica'
  });
  return this.callSuper('toSVG') + label.toSVG();
}

The "fontSize", "fill", and "fontFamily" should ideally be moved to an instance-level property of course, to avoid repeating them there.

Here's a modified test — http://jsfiddle.net/fabricjs/QPDy5/1/

What @Sergiu Paraschiv suggested with a group is another just-as-viable solution.




回答2:


Problem is there's no SVG equivalent to fillText so FabricJS seems to ignore it. My workaround is to use a fabric.Group and build an inner Rect and Text

var LabeledRect = function(options) {
    var rect = new fabric.Rect({
        width: options.width,
        height: options.height,
        fill: options.fill
    });

    var label = new fabric.Text(options.label);

    var group = new fabric.Group([rect, label]);

    group.left = options.left;
    group.top = options.top;

    group.toSVG = function() {
        var e=[];
        for(var t = 0; t < this._objects.length; t++)
            e.push(this._objects[t].toSVG());

        return'<g transform="'+this.getSvgTransform()+'">'+e.join("")+"</g>"
    };

    return group;
};

The reason I overwrote toSVG is because the default implementation cycles through the children in reverse order (for(var t=this._objects.length;t--;)). I have no clue why it does that, but the text renders underneath the rectangle.



来源:https://stackoverflow.com/questions/18848362/tosvg-method-not-working-on-extended-class-with-fabric-js

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!