Reputation: 1199
I have a small fabricjs scene which loads images and clips them to SVG shapes that are defined in an SVG layout. The clipping is done using the code that is specified in this stack.
I have two issues:
When I export to SVG (see the "export to SVG" button), the clipping does not persist. Is this possible, perhaps to export to SVG maybe by converting the clippings to in SVG?
I have a SVG string inside the let clippingSVG
variable. Is it possible to apply this SVG as another clipPath for the complete canvas (including export possibility), or the group of images?
Thanks in advance
Upvotes: 2
Views: 3671
Reputation: 2363
Thank you AndreaBogazzi for starting me off on the right foot. I'd prefer to use a subclass of fabric.Image rather than replace a couple of its prototype methods. Cut-and-paste can be dangerous; in fact, the newest version of fabricjs is already incompatible with the that solution. So, here's a subclass version of the same solution. In my case I'm always using a fixed clipPath so I removed all references to fixed.
const ClippedImage = fabric.util.createClass(fabric.Image, {
type: 'ClippedImage',
initialized: function(options) {
options || (options = {})
this.callSuper('initialize', options)
},
_render: function(ctx) {
if (this.clipPath) {
ctx.save()
var retina = this.canvas.getRetinaScaling()
ctx.setTransform(retina, 0, 0, retina, 0, 0)
ctx.transform.apply(ctx, this.canvas.viewportTransform)
this.clipPath.transform(ctx)
this.clipPath._render(ctx)
ctx.restore()
ctx.clip()
}
this.callSuper('_render', ctx)
},
toSVG: function(reviver) {
let result = this.callSuper('toSVG')
if(this.clipPath) {
const clipId = `Clipped${fabric.Object.__uid++}`
result = `
<clipPath id="${clipId}">
${this.clipPath.toSVG(reviver)}
</clipPath>
<g clip-path="url(#${clipId})">${result}</g>`
}
return reviver ? reviver(result) : result
}
})
Upvotes: 0
Reputation: 14731
This is rough implementation of top of the other answer that propose a toSVG method change to make clip ( fixed ) respected in toSVG.
The non fixed case is harder, i hope this helps.
var img01URL = 'http://fabricjs.com/assets/printio.png';
var img02URL = 'http://fabricjs.com/lib/pug.jpg';
var img03URL = 'http://fabricjs.com/assets/ladybug.png';
var img03URL = 'http://fabricjs.com/assets/ladybug.png';
function toSvg() {
document.getElementById('svg').innerHTML = canvas.toSVG();
}
fabric.Image.prototype.toSVG = function(reviver) {
var markup = this._createBaseSVGMarkup(), x = -this.width / 2, y = -this.height / 2, clipUrl = '';
if (this.clipPath) {
var id = fabric.Object.__uid++;
if (this.clipPath.fixed) {
markup.push('<clipPath id="myClip' + id + '">\n',
this.clipPath.toSVG(reviver),
'</clipPath>\n');
}
clipUrl = ' clip-path="url(#myClip' + id + ')" ';
}
markup.push('<g ', clipUrl, '>',
'<g transform="', this.getSvgTransform(), this.getSvgTransformMatrix(), '"', '>\n',
'\t<image ', this.getSvgId(), 'xlink:href="', this.getSvgSrc(true),
'" x="', x, '" y="', y,
'" style="', this.getSvgStyles(),
// we're essentially moving origin of transformation from top/left corner to the center of the shape
// by wrapping it in container <g> element with actual transformation, then offsetting object to the top/left
// so that object's center aligns with container's left/top
'" width="', this.width,
'" height="', this.height,
'"></image>\n'
);
if (this.stroke || this.strokeDashArray) {
var origFill = this.fill;
this.fill = null;
markup.push(
'<rect ',
'x="', x, '" y="', y,
'" width="', this.width, '" height="', this.height,
'" style="', this.getSvgStyles(),
'"/>\n'
);
this.fill = origFill;
}
markup.push('</g></g>\n');
return reviver ? reviver(markup.join('')) : markup.join('');
};
fabric.Image.prototype._render = function(ctx) {
// custom clip code
if (this.clipPath) {
ctx.save();
if (this.clipPath.fixed) {
var retina = this.canvas.getRetinaScaling();
ctx.setTransform(retina, 0, 0, retina, 0, 0);
// to handle zoom
ctx.transform.apply(ctx, this.canvas.viewportTransform);
//
this.clipPath.transform(ctx);
}
this.clipPath._render(ctx);
ctx.restore();
ctx.clip();
}
// end custom clip code
var x = -this.width / 2, y = -this.height / 2, elementToDraw;
if (this.isMoving === false && this.resizeFilter && this._needsResize()) {
this._lastScaleX = this.scaleX;
this._lastScaleY = this.scaleY;
this.applyResizeFilters();
}
elementToDraw = this._element;
elementToDraw && ctx.drawImage(elementToDraw,
0, 0, this.width, this.height,
x, y, this.width, this.height);
this._stroke(ctx);
this._renderStroke(ctx);
};
var canvas = new fabric.Canvas('c');
canvas.setZoom(0.5)
fabric.Image.fromURL(img01URL, function(oImg) {
oImg.scale(.25);
oImg.left = 10;
oImg.top = 10;
oImg.clipPath = new fabric.Circle({radius: 40, top: 50, left: 50, fixed: true, fill: '', stroke: '' });
canvas.add(oImg);
canvas.renderAll();
});
fabric.Image.fromURL(img02URL, function(oImg) {
oImg.scale(.40);
oImg.left = 180;
oImg.top = 0;
oImg.clipPath = new fabric.Path('M85.6,606.2c-13.2,54.5-3.9,95.7,23.3,130.7c27.2,35-3.1,55.2-25.7,66.1C60.7,814,52.2,821,50.6,836.5c-1.6,15.6,19.5,76.3,29.6,86.4c10.1,10.1,32.7,31.9,47.5,54.5c14.8,22.6,34.2,7.8,34.2,7.8c14,10.9,28,0,28,0c24.9,11.7,39.7-4.7,39.7-4.7c12.4-14.8-14-30.3-14-30.3c-16.3-28.8-28.8-5.4-33.5-11.7s-8.6-7-33.5-35.8c-24.9-28.8,39.7-19.5,62.2-24.9c22.6-5.4,65.4-34.2,65.4-34.2c0,34.2,11.7,28.8,28.8,46.7c17.1,17.9,24.9,29.6,47.5,38.9c22.6,9.3,33.5,7.8,53.7,21c20.2,13.2,62.2,10.9,62.2,10.9c18.7,6.2,36.6,0,36.6,0c45.1,0,26.5-15.6,10.1-36.6c-16.3-21-49-3.1-63.8-13.2c-14.8-10.1-51.4-25.7-70-36.6c-18.7-10.9,0-30.3,0-48.2c0-17.9,14-31.9,14-31.9h72.4c0,0,56-3.9,70.8,26.5c14.8,30.3,37.3,36.6,38.1,52.9c0.8,16.3-13.2,17.9-13.2,17.9c-31.1-8.6-31.9,41.2-31.9,41.2c38.1,50.6,112-21,112-21c85.6-7.8,79.4-133.8,79.4-133.8c17.1-12.4,44.4-45.1,62.2-74.7c17.9-29.6,68.5-52.1,113.6-30.3c45.1,21.8,52.9-14.8,52.9-14.8c15.6,2.3,20.2-17.9,20.2-17.9c20.2-22.6-15.6-28-16.3-84c-0.8-56-47.5-66.1-45.1-82.5c2.3-16.3,49.8-68.5,38.1-63.8c-10.2,4.1-53,25.3-63.7,30.7c-0.4-1.4-1.1-3.4-2.5-6.6c-6.2-14-74.7,30.3-74.7,30.3s-108.5,64.2-129.6,68.9c-21,4.7-18.7-9.3-44.3-7c-25.7,2.3-38.5,4.7-154.1-44.4c-115.6-49-326,29.8-326,29.8s-168.1-267.9-28-383.4C265.8,13,78.4-83.3,32.9,168.8C-12.6,420.9,98.9,551.7,85.6,606.2z',{top: 0, left: 180, fixed: true, fill: '', stroke: '', scaleX: 0.2, scaleY: 0.2 });
canvas.add(oImg);
canvas.renderAll();
});
#c {
border:1px solid #ccc;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.17/fabric.min.js"></script>
<button onClick='toSvg();'>TOSVG</button>
<canvas id="c" width="400" height="400"></canvas>
<div id="svg"></div>
Upvotes: 2