Mask two images using fabric js - javascript

Is it possible to mask two images using fabric js?
For example: I have a colored shape(heart). I have another images and I want to mask it over heart such that that new image is only displayed as heart. I can move the second uploaded image around and resize to get the desired area in that heart.
Both images are user uploaded images and not one of those rectangles or square which are created using fabric and drag and resize image like in the jsfiddle example here.
http://jsfiddle.net/Jagi/efmbrm4v/1/
var canvas = new fabric.Canvas('myCanvas');
var clippingRect = new fabric.Rect({
left: 0,
top: 0,
width: 100,
height: 100,
fill: 'transparent',
opacity: 1
});
canvas.add(clippingRect);
function handleImage(e) {
var reader = new FileReader();
reader.onload = function (event) {
var img = new Image();
img.onload = function () {
var instanceWidth, instanceHeight;
instanceWidth = img.width;
instanceHeight = img.height;
var imgInstance = new fabric.Image(img, {
width: instanceWidth,
height: instanceHeight,
top: (canvas.getHeight() / 2 - instanceHeight / 2),
left: (canvas.getWidth() / 2 - instanceWidth / 2),
originX: 'left',
originY: 'top'
});
canvas.add(imgInstance);
imgInstance.clipTo = function (ctx) {
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.rect(
100, 100,
200, 200
);
ctx.restore();
};
canvas.renderAll();
};
img.src = event.target.result;
};
reader.readAsDataURL(e.target.files[0]);
}
I need exactly this but with two custom images(user uploaded images).

Maybe this pseudo example will give you pointer how to add mask with a path, not just a rectangle.
//adding main image and mask
fabric.Image.fromURL(IMAGE_URL_HERE, function(pic) {
canvas.add(pic);
//positioning image, if needed
pic.centerH().setCoords();
//adding path, may be a heart or any other in your case
var path = new fabric.Path('M1.398,84.129c3.305,20.461,9.73,50.635,13.591,67.385c2.354,10.212,12.549,23.734,18.51,30.02c4.923,5.191,27.513,23.343,38.34,27.589c18.604,7.295,33.984,4.187,46.012-8.306c12.028-12.493,25.595-34.78,27.954-43.994c2.587-10.105,5.065-26.842,4.313-37.243c-1.036-14.316-14.224-69.332-16.806-79.55c-4.48-17.735-26.246-48.821-80.609-37.668C-1.66,13.514-1.879,63.844,1.398,84.129z');
//positioning path, if needed
path.set({
originX: 'center',
originY: 'center',
});
//masking img with path
pic.set({
clipTo: function(ctx) {
path.render(ctx);
}
});
canvas.renderAll();
});
Full demo (for slightly another case though) here: http://jsfiddle.net/mqL55m0f/2/

Related

Offset issues images when inside group (scaleToWidth)

I'm experiencing an issue when scaling an image inside a group the offset of the image changes. The group consists of a background (Rect) and an image. When scaling the group the offset if the image becomes different. Expected is that the offset remains the same as the background.
I'm using Fabric 4.0.0-beta.6
const canvas = new fabric.Canvas('canvas');
canvas.setWidth(document.body.clientWidth);
canvas.setHeight(document.body.clientHeight);
const bg = new fabric.Rect({
width: 100,
height: 100,
fill: 'red',
});
fabric.Image.fromURL("https://dummyimage.com/300x150/000/fff", function(img) {
img.scaleToWidth(bg.width)
const post = new fabric.Group([bg, img], {
left: 100,
top: 100,
});
post.scaleToWidth(400);
canvas.add(post);
canvas.renderAll();
});
https://jsbin.com/migogekoxu/1/edit?html,css,js,output
Setting the strokeWidth property of the background element to 0 fixed the offset for me.
const canvas = new fabric.Canvas('canvas');
canvas.setWidth(document.body.clientWidth);
canvas.setHeight(document.body.clientHeight);
const bg = new fabric.Rect({
width: 100,
height: 100,
fill: 'red',
strokeWidth: 0, //<---
});
fabric.Image.fromURL("https://dummyimage.com/300x150/000/fff", function(img) {
img.scaleToWidth(bg.width)
const post = new fabric.Group([bg, img], {
left: 100,
top: 100,
});
post.scaleToWidth(400);
canvas.add(post);
canvas.renderAll();
});
<canvas id="canvas"></canvas>
<script src="https://cdn.jsdelivr.net/npm/fabric#4.0.0-beta.6-browser/dist/fabric.min.js"></script>
Obviously that's a bug with fabricJS. If you don't want to fix it on your own in it's source code you can workaround it however.
You can manually correct the position by adding an offset of 0.5 to the image's top and left properties.
For example:
const canvas = new fabric.Canvas('canvas');
const bg = new fabric.Rect({
width: 100,
height: 100,
fill: 'red'
});
fabric.Image.fromURL("https://dummyimage.com/300x150/000/fff", function(img) {
img.scaleToWidth(bg.width);
img.left += 0.5;
img.top += 0.5;
const post = new fabric.Group([bg, img], {
left: 100,
top: 100
});
post.scaleToWidth(400);
canvas.add(post);
canvas.renderAll();
});
<script src="https://cdn.jsdelivr.net/npm/fabric#4.0.0-beta.6-browser/dist/fabric.min.js"></script>
<canvas id="canvas" width="600" height="400"></canvas>

Save image from clipped rectangle in FabricJS

I'm using clipTo function to clip an background image inside a rectangle. Before using clipTo function,i was able to save image successfully from canvas with proper height and width.
But after introducing clipTo function, I'm able to save image, but the background image appeared in final(saved) image looks weird. The color of canvas also appeared in final image.
Following is the image of my canvas.
See my final image,it has 1200 X 1200 dimension. (Here I compressed to reduce the file size).
This is the script i'm using to add the image to canvas.
canvasImage(){
const base_image = new Image();
base_image.crossOrigin = 'Anonymous';
base_image.src = 'assets/images/wallpaper.jpg';
fabric.Image.fromURL(base_image.src, (myImg) => {
this.backgroundimage = myImg.set({left: 100, top: 80, id: 'wallpaper'});
const FakeCanvas = new fabric.Rect({
originX: 'left',
originY: 'top',
left: 100,
top: 0,
width: 600,
height: 600,
fill: '#fff',
objectCaching: false,
selectable: false
});
const retina = this.FabriCanvas.getRetinaScaling();
myImg.clipTo = function (ctx) {
ctx.save();
ctx.setTransform(retina, 0, 0, retina, 0, 0);
FakeCanvas.render(ctx);
ctx.restore();
};
myImg.scaleToHeight(800);
myImg.scaleToWidth(900);
this.FabriCanvas.add(this.backgroundimage).setActiveObject(this.backgroundimage);
const hiddenImg = document.createElement('img');
hiddenImg.src = this.FabriCanvas.getActiveObject().toDataURL();
hiddenImg.id = 'target';
hiddenImg.style.display = 'none';
document.body.appendChild(hiddenImg);
});
}
This is the code to save the image. I use multiplier:2 to get 1200 X 1200 dimension.
saveImage() {
const image = this.FabriCanvas.toDataURL({
left: 0,
top: 0,
width: 600,
height: 600,
format: 'png',
multiplier: 2
});
// api call
this.imageService.upload(image).subscribe((res)=> {
if(res.data.status === 200){
console.log('image uploaded successfully')
}else{
console.log('Image upload failed')
}
}
What i'm trying to achieve ?
Final image must consist only the background image and it should have dimension of 1200 X 1200.

Fit the background image to canvas size with Fabric js

I am trying to stretch the background image so it can fit the canvas size. But its not working.
I have followed this question and based on comment i have implemented the stretching code.
Strech the background image to canvas size with Fabric.js
http://fabricjs.com/docs/fabric.Canvas.html#setBackgroundImage
Code
$(function () {
var canvas1 = new fabric.Canvas('canvas1');
//Default
var canvas = canvas1;
//Change background using Image
document.getElementById('bg_image').addEventListener('change', function (e) {
canvas.setBackgroundColor('', canvas.renderAll.bind(canvas));
canvas.setBackgroundImage(0, canvas.renderAll.bind(canvas));
var file = e.target.files[0];
var reader = new FileReader();
reader.onload = function (f) {
var data = f.target.result;
fabric.Image.fromURL(data, function (img) {
img.set({
width: canvas.getWidth(),
height: canvas.getHeight(),
originX: 'left',
originY: 'top'
});
canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas));
/*canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas), {
width: canvas.getWidth(),
height: canvas.getHeight(),
originX: 'left',
originY: 'top',
crossOrigin: 'anonymous'
});*/
});
};
reader.readAsDataURL(file);
});
});
It seems that it is not working with latest beta version, however its working with 1.7.19 version but not with 2.0.0-beta.7
JSFiddle:
version 2.0.0-beta.7: http://jsfiddle.net/dhavalsisodiya/8vLo882n/3
varsion 1.7.19: http://jsfiddle.net/dhavalsisodiya/8vLo882n/4/
The solution given by #spankajd is almost there but still not 100% accurate (as you'll see the background image doesn't completely fit to the canvas).
The proper solution to this problem is indeed to use the scaleX and scaleY property (which takes a number as scaling factor), as mentioned by #asturur on your submitted issue.
However, in that case you won't need to set the width and height property of the image. Also, a better (and correct) way of setting those image properties is to pass them as options (argument) to the setBackgroundImage() method (this may resolve some performance issues), as such :
canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas), {
scaleX: canvas.width / img.width,
scaleY: canvas.height / img.height
});
... instead of setting them for the image object separately.
that being said, here is a complete working example :
$(function() {
var canvas1 = new fabric.Canvas('canvas1');
//Default
var canvas = canvas1;
canvas.backgroundColor = '#34AD39';
canvas.renderAll();
//Change background using Image
document.getElementById('bg_image').addEventListener('change', function(e) {
canvas.setBackgroundColor('', canvas.renderAll.bind(canvas));
canvas.setBackgroundImage(0, canvas.renderAll.bind(canvas));
var file = e.target.files[0];
var reader = new FileReader();
reader.onload = function(f) {
var data = f.target.result;
fabric.Image.fromURL(data, function(img) {
canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas), {
scaleX: canvas.width / img.width,
scaleY: canvas.height / img.height
});
});
};
reader.readAsDataURL(file);
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.0.0-beta.7/fabric.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="margin-top:20px;">
<label class="control-label">Add Background Image</label>
<input type="file" id="bg_image" />
</div>
<canvas id="canvas1" width="600" height="400" style="border:1px solid #000"></canvas>
Hi please update following code.
img.set({
width: canvas.getWidth(),
height: canvas.getHeight(),
originX: 'left',
scaleX : canvas.getWidth()/img.width, //new update
scaleY: canvas.getHeight()/img.height, //new update
originY: 'top'
});
http://jsfiddle.net/8vLo882n/6/

FabricJS v.2 and AngularJS – set canvas background image dynamically

I am using this function to define my fabricJS canvas background image:
$scope.bgImage = function () {
const {
ipcRenderer
} = require('electron')
ipcRenderer.send('openFile', () => {
})
ipcRenderer.once('fileData', (event, filepath) => {
fabric.Image.fromURL(filepath, function (image) {
var hCent = canvas.getHeight / 2;
var wCent = canvas.getWidth / 2;
canvas.setBackgroundColor({
source: filepath,
top: hCent,
left: wCent,
originX: 'center',
originY: 'middle',
repeat: 'no-repeat',
scaleX: 10,
scaleY: 10
}, canvas.renderAll.bind(canvas));
})
})
};
All the parameters shoulkd be change on the fly with some html inputs. Setting the repeat is working fine and i can change it dynamically.
But I am simply not able to change the scale of my background image. Using the above function I would expect that my background image would be:
would be not repeated (ok. is working)
positioned in the middle/center of my canvas (it is not)
would be scaled by a factor of 10 (it is not)
I am making again a newbish mistake? Or ist this realted to some changes in image handling in FabricJS v.2
canvas.setBackgroundImage() Use this function to add Image as a background.
To get Height/Width of canvas use canvas.getHeight()/canvas.getWidth(),these are functions. Or else you can use the properties > canvas.height/canvas.width respectively.
DEMO
// initialize fabric canvas and assign to global windows object for debug
var canvas = window._canvas = new fabric.Canvas('c');
var hCent = canvas.getHeight() / 2;
var wCent = canvas.getWidth() / 2;
canvas.setBackgroundImage('http://fabricjs.com/assets/jail_cell_bars.png', canvas.renderAll.bind(canvas), {
originX: 'center',
originY: 'center',
left: wCent,
top: hCent,
scaleX: 1,
scaleY: 1
});
fabric.util.addListener(document.getElementById('toggle-scaleX'), 'click', function () {
if (!canvas.backgroundImage) return;
canvas.backgroundImage.scaleX = canvas.backgroundImage.scaleX < 1 ? 1 : fabric.util.toFixed(Math.random(), 2);
canvas.renderAll();
});
fabric.util.addListener(document.getElementById('toggle-scaleY'), 'click', function () {
if (!canvas.backgroundImage) return;
canvas.backgroundImage.scaleY = canvas.backgroundImage.scaleY < 1 ? 1 : fabric.util.toFixed(Math.random(), 2);
canvas.renderAll();
});
canvas {
border: 1px solid #999;
}
button {
margin-top: 20px;
}
<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" width="600" height="600"></canvas>
<button id="toggle-scaleX">Toggle scaleX</button>
<button id="toggle-scaleY">Toggle scaleY</button>

Why objects which placed close without any indent, have a space between each other in FabricJS

I need to build a frame for my image in Fabric JS. This frame should be built dynamically to fit any size of the image.
To do this I use two images:
http://i.imgur.com/3VqKv1O.png - image for side
http://i.imgur.com/w41HYN3.png - image for corner
I've developed an algorithm, which does it and it works! That's great.
But I have a problem with the positioning of pieces of the frame.
As you can see in my example, there is strange space between pieces and they placed not on the same level by Y axis. I'm very surprised because these pieces have the same height and they have correct values for left and top.
Anybody knows what can be a reason of this problem?
Thanks in advance
JS Fiddle https://jsfiddle.net/Eugene_Ilyin/gzjxhLtm/9/
var sideObj = new fabric.Rect(),
frameSideTopImage = new fabric.Image(new Image(), {type: 'image'}),
frameCornerTLImage = new fabric.Image(new Image(), {type: 'image'}),
imageSize = 172,// Size of the image (width = height).
leftOffset = 100,
topOffset = 100;
// Load image for corner.
fabric.util.loadImage('http://i.imgur.com/w41HYN3.png', function (img) {
// Set image and options for top left corner.
frameCornerTLImage.setElement(img);
frameCornerTLImage.setOptions({
strokeWidth: 0,
left: leftOffset,
top: topOffset
});
// Load image for side.
fabric.util.loadImage('http://i.imgur.com/3VqKv1O.png', function (img) {
frameSideTopImage.setElement(img);
// Create canvas for pattern.
var canvasForFill = new fabric.StaticCanvas(fabric.util.createCanvasElement(), {enableRetinaScaling: false});
canvasForFill.add(frameSideTopImage);
// Configure top side for the frame.
sideObj.setOptions({
strokeWidth: 0,
objectCaching: false,
originX: 'left',
originY: 'top',
left: imageSize + 100,
top: 100,
width: 800,
height: imageSize,
fill: new fabric.Pattern({
source: function () {
canvasForFill.setDimensions({
width: imageSize,
height: imageSize
});
return canvasForFill.getElement();
},
repeat: 'repeat-x'
})
});
var canvas = new fabric.Canvas('c');
var frame = new fabric.Group([sideObj, frameCornerTLImage], {
strokeWidth: 0,
width: 800 * 3,
height: imageSize * 3,
scaleX: 1/3,
scaleY: 1/3,
}
);
canvas.add(frame);
canvas.renderAll();
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.7/fabric.js"></script>
<body>
<canvas id="c" width="1000" height="1000"></canvas>
</body>
var sideObj = new fabric.Rect(),
frameSideTopImage = new fabric.Image(new Image(), {type: 'image'}),
frameCornerTLImage = new fabric.Image(new Image(), {type: 'image'}),
imageSize = 172,// Size of the image (width = height).
leftOffset = 100,
topOffset = 100;
// Load image for corner.
fabric.util.loadImage('http://i.imgur.com/w41HYN3.png', function (img) {
// Set image and options for top left corner.
frameCornerTLImage.setElement(img);
frameCornerTLImage.setOptions({
left: leftOffset,
top: topOffset
});
// Load image for side.
fabric.util.loadImage('http://i.imgur.com/3VqKv1O.png', function (img) {
frameSideTopImage.setElement(img);
// Create canvas for pattern.
var canvasForFill = new fabric.StaticCanvas(fabric.util.createCanvasElement(), {enableRetinaScaling: false});
canvasForFill.add(frameSideTopImage);
// Configure top side for the frame.
sideObj.setOptions({
objectCaching: false,
originX: 'left',
originY: 'top',
left: imageSize + 100,
top: 100,
width: 800,
strokeWidth:0,
height: imageSize,
fill: new fabric.Pattern({
source: function () {
canvasForFill.setDimensions({
width: imageSize,
height: imageSize
});
return canvasForFill.getElement();
},
repeat: 'repeat-x'
})
});
var canvas = new fabric.Canvas('c');
canvas.add(sideObj);
canvas.add(frameCornerTLImage);
canvas.renderAll();
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.7/fabric.js"></script>
<body>
<canvas id="c" width="1000" height="1000"></canvas>
</body>
set strokeWidth:0 , strokeWidth is by default 1, so it creates a border of width 1 .

Categories

Resources