I'm trying to clip an image with a path but can't seem to find any help to do this with.
I've found examples to clip an image with a rectangle or circle, but a path example seems to be missing. I've also managed to clip the entire canvas with the path, but what I want is just clipping 1 image. Any help will be appreciated!
The following example is for clipping a canvas with the path.
var canvas = new fabric.Canvas('c');
canvas.setDimensions({width:_winWidth,height:_winHeight});
var path = new fabric.Path('M2.9,245h-5.8c-35.3,0-69.3-14.4-93.3-39.4c-23.3-24.3-35.2-56.4-33.7-90.4c1.8-40.1,21-68.9,39.6-91.8l2.2-2.8\
c10.6-13,22.6-27.8,23.6-43.4c0.9-14.6-7.1-27.8-15-38.9c-14.5-20.3-31-43.3-31-74.5c-0.1-28,11.5-55.1,32.4-76.1\
C-57.2-233.1-29.8-245-2.9-245h5.8c27,0,54.3,11.9,75.1,32.7c21,21,32.5,48,32.4,76C110.4-105,93.9-82,79.4-61.7\
c-7.9,11.1-15.9,24.4-15,38.9c1,15.6,13,30.4,23.6,43.4l2.2,2.8c18.6,22.9,37.8,51.7,39.6,91.8c1.5,34-10.4,66.2-33.7,90.4\
C72.2,230.6,38.2,245,2.9,245z');
path.centeredScaling = true;
path.scale(1);
//path.selectable = true;
path.originX = 'center';
path.originY = 'center';
path.set({ left: (_winWidth/2), top: (_winHeight/2)});
fabric.Image.fromURL('../images/home/2.jpg', function(img) {
img.selectable = false;
img.scale(1.2);
canvas.centerObject(img);
canvas.add(img);
canvas.clipTo = function (ctx) {
path.render(ctx);
}
canvas.renderAll();
});
canvas = new fabric.Canvas('fabric', { selection: false });
var path = new fabric.Path('M 272.70141,238.71731 \
C 206.46141,238.71731 152.70146,292.4773 152.70146,358.71731 \
C 152.70146,493.47282 288.63461,528.80461 381.26391,662.02535 \
C 468.83815,529.62199 609.82641,489.17075 609.82641,358.71731 \
C 609.82641,292.47731 556.06651,238.7173 489.82641,238.71731 \
C 441.77851,238.71731 400.42481,267.08774 381.26391,307.90481 \
C 362.10311,267.08773 320.74941,238.7173 272.70141,238.71731 \
z ');
var scale = 100 / path.width;
path.set({ left: 20, top: 0, scaleX: scale, scaleY: scale, fill: 'red', });
canvas.add(path);
addPattern(path);
canvas.renderAll();
function addPattern(obj){
fabric.util.loadImage('http://fabricjs.com/assets/pug_small.jpg', function (img) {
obj.fill = new fabric.Pattern({
source: img,
repeat: 'no-repeat'
});
canvas.renderAll();
});
}
To clip a single image, try this:-
fabric.Image.fromURL('http://fabricjs.com/assets/pug_small.jpg', function(img) {
img.scale(0.5).set({
left: 100,
top: 100,
angle: -15,
clipTo: function (ctx) {
path.render(ctx);
}
});
canvas.add(img).setActiveObject(img);
});
Note: I'd use var myPath = xxxx instead since 'path' is too close to SVG's path
Related
I updated fabricjs for new control feature.
But my old image clipping is not working anymore since clipTo in fabric.Object is removed in new version.
How can I clip image without using clipTo, in change log they said I should use clipPath instead.
img.set({
clipTo: function (ctx) {
ctx.moveTo(0, 0);
ctx.arc(0, 0,300, -Math.PI/6, -Math.PI+Math.PI/6 , true);
}
});
Here is jsfiddle
Also this official demo won't work in version 4 beta
http://fabricjs.com/clipping
So, clipTo is deprecated since version 2 and was removed in version 4. The correct way of clipping is to use the clipPath property. Here is a simple example:
var radius = 150;
var clipPath = new fabric.Circle({
left: 0,
top: 0,
radius: radius,
startAngle: 0,
angle: Math.PI * 2,
originX: "top"
});
fabric.Image.fromURL("../public/pug_small.jpg", function(img) {
img.scale(0.5).set({
left: 100,
top: 100,
angle: -15,
clipPath: clipPath
});
canvas.add(img).setActiveObject(img);
});
Here is the official tutorial for the clipPath http://fabricjs.com/clippath-part1
Recent discussion about clipPath in the beta 4 version: https://github.com/fabricjs/fabric.js/issues/6159
And a sample SandBox demo: https://codesandbox.io/s/stackoverflow-60664120-fabric-js-400-beta8-mi0y7
I found temporary solution but it is not really answer:
I draw circle and polygon
var radius = 100;
var start = -150*Math.PI/180
var end = -30*Math.PI/180
let point1 = new fabric.Point(
(radius+1)*Math.cos(start),
(radius+1)*Math.sin(start)
)
let point2 = new fabric.Point(
(radius+1)*Math.cos(end),
(radius+1)*Math.sin(end)
)
fabric.Image.fromURL('http://fabricjs.com/assets/pug_small.jpg', function (img) {
img.scale(1).set({
left: 0,
top: 0,
clipPath:new fabric.Group([
new fabric.Circle({
originX:'center',
originY:'center',
radius,
startAngle: start,
endAngle: end,
stroke:false,
strokeWidth:6
}),
new fabric.Polygon([point1,{x:0,y:0},point2],{
originX:'center',
originY:'center',
strokeWidth:6
})
])
);
canvas.add(img).setActiveObject(img);
});
http://jsfiddle.net/mudin/z75nvgqs/31/
Help me to find better solution
I loaded multiple objects from svg by following code:
var canvas = new fabric.Canvas('drawing');
fabric.loadSVGFromURL('images/circle.svg', function(objects) {
canvas.add.apply(canvas, objects);
canvas.forEachObject(function(o){ o.hasBorders = o.hasControls = false;
});
canvas.renderAll();
});
Now I want to free draw only inside one object(like the image below).How can I achieve that using fabric.js?
You can mask the canvas (using canvas.clipTo) and make it match your SVG form. If it is a circle as in your example image it would be simple.
See the following example:
// define a drawing canvas
const canvas = new fabric.Canvas(
'drawing',
{ isDrawingMode: true }
);
canvas.freeDrawingBrush.color = 'red'
// create a circle (here you could load your svg circle instead)
const circle = new fabric.Circle({
top: 25,
left: 25,
radius: 50,
fill: 'transparent',
stroke: 'black'
});
// create the clipping mask using the circle coordinates
canvas.clipTo = function (ctx) {
ctx.arc(
circle.top + circle.radius,
circle.left + circle.radius,
circle.radius + 1, // +1px for the circle stroke
circle.radius + 1,
Math.PI*2,
true
)
}
// add the circle to the canvas
canvas.add(circle);
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.5/fabric.min.js"></script>
<canvas id="drawing" width="600" height="600"></canvas>
Is there any option to add pattern with Image AND Color?
Anytime, when I apply an image as a pattern, even though the image is transparent, Fabric adds it with some kind of "gray layer".
Link to Fiddler
var canvas = window.canvas = new fabric.Canvas('c');
var rect = new fabric.Rect({
width: 256,
height: 256,
fill: 'red'
});
canvas.add(rect);
rect.center().setCoords();
fabric.util.loadImage('https://assets-cdn.github.com/images/modules/site/integrators/slackhq.png', function (img) {
rect.setPatternFill({
source: img,
repeat: 'no-repeat'
});
canvas.renderAll();
});
Any idea?
Assuming the image has transparent background, you can use fabric.StaticCanvas as fabric.Pattern's source:
var imgPath = 'https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-icon.png'
var imgColor = 'grey'
var canvas = this.__canvas = new fabric.Canvas('c')
fabric.Image.fromURL(imgPath, function(img) {
var patternSourceCanvas = new fabric.StaticCanvas()
patternSourceCanvas.add(img)
patternSourceCanvas.setBackgroundColor(imgColor, patternSourceCanvas.renderAll.bind(patternSourceCanvas))
var pattern = new fabric.Pattern({
source: function() {
return patternSourceCanvas.getElement();
},
repeat: 'repeat'
})
// create a rectangle object
var rect = new fabric.Rect({
fill: pattern,
width: 400,
height: 400
})
// "add" rectangle onto canvas
canvas.add(rect)
})
<script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.9/fabric.min.js"></script>
<canvas id="c" width="500" height="500"></canvas>
I just want to know what is the best way to use multiple canvas in a single page. These canvas can be overlapped on each other.
I tried to search this issue on different form, but wasn't able to find any helpful material. This is what we actually want to do(in the following image). There are 5 canvases, and we want all of them to be fully functional. We can add images, text and draw different things on selected canvas.
We are currently using fabricjs.
If that`s not possible, what is the best solution for achieving something like that ?
Thanks in advance!
Simply use CSS for that.
<div class="wrapper">
<canvas id="background_layer" class="canvas-layer" width="100" height="100"></canvas>
<canvas id="other_layer" class="canvas-layer" width="100" height="100"></canvas>
</div>
<style>
.wrapper { position: relative }
.canvas-layer {
position: absolute; left: 0; top: 0;
}
</style>
I am not sure what you are trying to achieve but you can refer to this Fiddle http://jsfiddle.net/PromInc/ZxYCP/
var img01URL = 'https://www.google.com/images/srpr/logo4w.png';
var img02URL = 'http://fabricjs.com/lib/pug.jpg';
var canvas = new fabric.Canvas('c');
// Note the use of the `originX` and `originY` properties, which we set
// to 'left' and 'top', respectively. This makes the math in the `clipTo`
// functions a little bit more straight-forward.
var clipRect1 = new fabric.Rect({
originX: 'left',
originY: 'top',
left: 180,
top: 10,
width: 200,
height: 200,
fill: '#DDD', /* use transparent for no fill */
strokeWidth: 0,
selectable: false
});
// We give these `Rect` objects a name property so the `clipTo` functions can
// find the one by which they want to be clipped.
clipRect1.set({
clipFor: 'pug'
});
canvas.add(clipRect1);
var clipRect2 = new fabric.Rect({
originX: 'left',
originY: 'top',
left: 10,
top: 10,
width: 150,
height: 150,
fill: '#DDD', /* use transparent for no fill */
strokeWidth: 0,
selectable: false
});
// We give these `Rect` objects a name property so the `clipTo` functions can
// find the one by which they want to be clipped.
clipRect2.set({
clipFor: 'logo'
});
canvas.add(clipRect2);
function findByClipName(name) {
return _(canvas.getObjects()).where({
clipFor: name
}).first()
}
// Since the `angle` property of the Image object is stored
// in degrees, we'll use this to convert it to radians.
function degToRad(degrees) {
return degrees * (Math.PI / 180);
}
var clipByName = function (ctx) {
this.setCoords();
var clipRect = findByClipName(this.clipName);
var scaleXTo1 = (1 / this.scaleX);
var scaleYTo1 = (1 / this.scaleY);
ctx.save();
var ctxLeft = -( this.width / 2 ) + clipRect.strokeWidth;
var ctxTop = -( this.height / 2 ) + clipRect.strokeWidth;
var ctxWidth = clipRect.width - clipRect.strokeWidth;
var ctxHeight = clipRect.height - clipRect.strokeWidth;
ctx.translate( ctxLeft, ctxTop );
ctx.rotate(degToRad(this.angle * -1));
ctx.scale(scaleXTo1, scaleYTo1);
ctx.beginPath();
ctx.rect(
clipRect.left - this.oCoords.tl.x,
clipRect.top - this.oCoords.tl.y,
clipRect.width,
clipRect.height
);
ctx.closePath();
ctx.restore();
}
var pugImg = new Image();
pugImg.onload = function (img) {
var pug = new fabric.Image(pugImg, {
angle: 45,
width: 500,
height: 500,
left: 230,
top: 50,
scaleX: 0.3,
scaleY: 0.3,
clipName: 'pug',
clipTo: function(ctx) {
return _.bind(clipByName, pug)(ctx)
}
});
canvas.add(pug);
};
pugImg.src = img02URL;
var logoImg = new Image();
logoImg.onload = function (img) {
var logo = new fabric.Image(logoImg, {
angle: 0,
width: 550,
height: 190,
left: 50,
top: 50,
scaleX: 0.25,
scaleY: 0.25,
clipName: 'logo',
clipTo: function(ctx) {
return _.bind(clipByName, logo)(ctx)
}
});
canvas.add(logo);
};
logoImg.src = img01URL;
I hope this might help.
I have the path of a heart (below), but when I draw it on the canvas with fabric.js, the boundingbox is not displayed correctly. This happens even if I use setCoords(). Moreover, when I export the object to svg, the location of the heart is at the top of the boundingbox instead of the location it is displayed on the canvas.
var path = new fabric.Path("M248.078,5.883c-36.691-14.739-77.771-0.839-98.517,31.125C128.817,5.044,87.735-8.856,51.043,5.883 C9.354,22.632-10.863,70.009,5.887,111.696c16.06,39.98,143.314,139.607,143.314,139.607l0.359,0.28l0.36-0.28 c0,0,127.251-99.627,143.314-139.607C309.986,70.009,289.768,22.632,248.078,5.883z");
The jsfiddle, http://jsfiddle.net/mPhL2/
Does anyone know how to solve this, is this a bug?
Thanks!
It has something to do with the coordinates of the path. Let's do a simple example.
The blue triangle is defined with absolute coordinates and the selection if fine
It has been produced with
canvas = new fabric.Canvas('myCanvas', { selection: false });
var path = new fabric.Path('M 0 0 L 0 100 L 100 100 Z');
path.set({ left: 20, top: 0, fill: 'blue', });
canvas.add(path);
canvas.renderAll();
If we take relative coordinates as with the green triangle then the selection does not surround the object properly anymore.
canvas = new fabric.Canvas('myCanvas', { selection: false });
var path = new fabric.Path('M 20 20 l 0 100 l 100 0 Z');
path.set({ left: 20, top: 0, fill: 'green', });
canvas.add(path);
canvas.renderAll();
So if we take another path for the heart (copied out from wikipedia) it works.
canvas = new fabric.Canvas('myCanvas', { selection: false });
var path = new fabric.Path('M 272.70141,238.71731 \
C 206.46141,238.71731 152.70146,292.4773 152.70146,358.71731 \
C 152.70146,493.47282 288.63461,528.80461 381.26391,662.02535 \
C 468.83815,529.62199 609.82641,489.17075 609.82641,358.71731 \
C 609.82641,292.47731 556.06651,238.7173 489.82641,238.71731 \
C 441.77851,238.71731 400.42481,267.08774 381.26391,307.90481 \
C 362.10311,267.08773 320.74941,238.7173 272.70141,238.71731 \
z ');
var scale = 100 / path.width;
path.set({ left: 20, top: 0, scaleX: scale, scaleY: scale, fill: 'red', });
canvas.add(path);
canvas.renderAll();