Reputation: 3540
I am using the HTML5 CANVAS with fabric js. Finally will be converted it into SVG.we can upload images and crop into circle,rectangle,.. .The image also being cropped and added text on it.The problem which i am facing is the images are not being cropped into svg.It shows full images like below.i have tried viewBox
also in toSVG
.Please suggest me what to do or if i am doing anything wrong.
$(function(){
var canvas = new fabric.Canvas('Canvas',{backgroundColor: '#ffffff',preserveObjectStacking: true});
canvas.clipTo = function (ctx) {
ctx.arc(250, 300, 200, 0, Math.PI*2, true);
};
fabric.Image.fromURL('https://fabric-canvas.s3.amazonaws.com/Tulips.jpg', function(oImg) {
canvas.add(oImg);
});
canvas.add(new fabric.IText('Welcome ', {
left : fabric.util.getRandomInt(50,50),
top:fabric.util.getRandomInt(430, 430)
}));
canvas.renderAll();
$('#tosvg_').on('click',function(){
$('#svgcontent').html(canvas.toSVG());
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.18/fabric.js"></script>
<div class="container">
<div id='canvascontain' width='1140' height='600' style='left:0px;background-color:rgb(240,240,240)'>
<canvas id="Canvas" width='1140' height='600'></canvas>
</div>
<input type='button' id='tosvg_' value='create SVG'>
<div id='svgcontent'></div>
Upvotes: 0
Views: 1624
Reputation: 5468
I looked through the fabric documentation and could not find any methods for carrying over the canvas's clipTo
property to the generated SVG. So I think the best (only) way to crop the SVG image to a circle, is to insert some additional SVG elements into it. Rather than explaining piece by piece, I'll just paste some commented JS code I came up with for this.
$(function(){
var canvas = new fabric.Canvas('Canvas',{backgroundColor: '#ffffff',preserveObjectStacking: true});
canvas.clipTo = function (ctx) {
ctx.arc(250, 300, 200, 0, Math.PI*2, true);
};
// Using a JQuery deferred object as a promise. This is used to
// synchronize execution, so that the SVG is never created before
// the image is added to the canvas.
var dImageLoaded = $.Deferred();
fabric.Image.fromURL('https://fabric-canvas.s3.amazonaws.com/Tulips.jpg', function(oImg) {
// Insert the image object below any existing objects, so that
// they appear on top of it. Then resolve the deferred.
canvas.insertAt(oImg, 0);
dImageLoaded.resolve();
});
canvas.add(new fabric.IText('Welcome ', {
left : fabric.util.getRandomInt(50,50),
top:fabric.util.getRandomInt(430, 430)
}));
canvas.renderAll();
$('#tosvg_').on('click',function(){
// Wait for the deferred to be resolved.
dImageLoaded.then(function () {
// Get the SVG string from the canvas.
var svgString = canvas.toSVG({
viewBox: {
x: 50,
y: 100,
width: 400,
height: 400,
},
width: 400,
height: 400,
});
// SVG content is not HTML, nor even standard XML, so doing
// $(svgString) might mutilate it. Therefore, even though we
// will be modifying the SVG before displaying it, we need to
// insert it straight into the DOM before we can get a JQuery
// selector to it.
// The plan is to hide the svgcontent div, append the SVG,
// modify it, and then show the svcontent div, to prevent the
// user from seeing half-baked SVG.
var $svgcontent = $('#svgcontent').hide().html(svgString);
var $svg = $svgcontent.find('svg');
// Create some SVG elements to represent the clip path, and
// append them to $svg. To create SVG elements, we need to use
// document.createElementNS, not document.createElement, which
// is also what $('<clipPath>') and $('<circle>') would call.
var $clipPath = $(document.createElementNS('http://www.w3.org/2000/svg', 'clipPath'))
.attr('id', 'clipCircle')
.appendTo($svg);
var $circle = $(document.createElementNS('http://www.w3.org/2000/svg', 'circle'))
.attr('r', 200)
.attr('cx', 250)
.attr('cy', 300)
.appendTo($clipPath);
// This part is brittle, since it blindly writes to the
// transform attributes of the image and text SVG elements.
// SVG clip paths clip content relative to the pre-transformed
// coordinates of the element. The canvas.toSVG method places
// the image and the text each inside of group elements, and
// transforms the group elements. It does not transform the
// image and text. Because transforms work equally well on
// image and text as on groups, we move the transform attribute
// from the two groups to their contents. Then, we can use the
// same clip-path on all groups.
// If the contents of the groups already had transforms, we
// would need to create multiple clip paths - one for each group
// - based on the groups' transforms.
$svg.find('g').each(function () {
var $g = $(this);
// Move transform attribute from group to its children, and
// give the group a clip-path attribute.
$g.children().attr('transform', $g.attr('transform'));
$g.removeAttr('transform').attr('clip-path', 'url(#clipCircle)');
});
$('#svgcontent').show();
});
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.18/fabric.js"></script>
<div class="container">
<div id='canvascontain' width='1140' height='600' style='left:0px;background-color:rgb(240,240,240)'>
<canvas id="Canvas" width='1140' height='600'></canvas>
</div>
<input type='button' id='tosvg_' value='create SVG'>
<div id='svgcontent'></div>
</div>
I noticed that part of the Welcome text gets clipped, but I'll leave it to you to move that.
Upvotes: 3