I implemented image cropping using FabricJS and clipPath property.
The question is how do I make the image fit the canvas after cropping? I want the cropped image to fill the canvas area but can't figure out whether it's possible to do using fabric js.
So I want the selected part of the image to fit the canvas size after the user clicks the Crop button:
About the code: I draw a rectangle on the canvas and the user can resize and move it. After that, the user can click the Crop button and get a cropped image. But the cropped part stays the same size it was on the original image whereas I want it to scale and fit the canvas.
Note: I tried to use methods like scaleToWidth. Additionally, I used absolutePositioned set to true for the selection rectangle. I tried to scale to image with this property set to false, but it didn't help.
Please do not suggest using cropX and cropY properties for cropping instead of clipPath as they don't work properly for rotated images.
HTML:
<button>Crop</button>
<canvas id="canvas" width="500" height="500"></canvas>
JS:
// crop button
var button = $("button");
// handle click
button.on("click", function(){
let rect = new fabric.Rect({
left: selectionRect.left,
top: selectionRect.top,
width: selectionRect.getScaledWidth(),
height: selectionRect.getScaledHeight(),
absolutePositioned: true
});
currentImage.clipPath = rect;
canvas.remove(selectionRect);
canvas.renderAll();
});
var canvas = new fabric.Canvas('canvas');
canvas.preserveObjectStacking = true;
var selectionRect;
var currentImage;
fabric.Image.fromURL('.512509!/image/DiamondBasement.jpg', img => {
img.scaleToHeight(500);
img.selectable = true;
canvas.add(img);
canvas.centerObject(img);
currentImage = img;
canvas.backgroundColor = "#333";
addSelectionRect();
canvas.setActiveObject(selectionRect);
canvas.renderAll();
});
function addSelectionRect() {
selectionRect = new fabric.Rect({
fill: 'rgba(0,0,0,0.3)',
originX: 'left',
originY: 'top',
stroke: 'black',
opacity: 1,
width: currentImage.width,
height: currentImage.height,
hasRotatingPoint: false,
transparentCorners: false,
cornerColor: 'white',
cornerStrokeColor: 'black',
borderColor: 'black',
});
selectionRect.scaleToWidth(300);
canvas.centerObject(selectionRect);
selectionRect.visible = true;
canvas.add(selectionRect);
}
Here is my jsfiddle: /
I implemented image cropping using FabricJS and clipPath property.
The question is how do I make the image fit the canvas after cropping? I want the cropped image to fill the canvas area but can't figure out whether it's possible to do using fabric js.
So I want the selected part of the image to fit the canvas size after the user clicks the Crop button:
About the code: I draw a rectangle on the canvas and the user can resize and move it. After that, the user can click the Crop button and get a cropped image. But the cropped part stays the same size it was on the original image whereas I want it to scale and fit the canvas.
Note: I tried to use methods like scaleToWidth. Additionally, I used absolutePositioned set to true for the selection rectangle. I tried to scale to image with this property set to false, but it didn't help.
Please do not suggest using cropX and cropY properties for cropping instead of clipPath as they don't work properly for rotated images.
HTML:
<button>Crop</button>
<canvas id="canvas" width="500" height="500"></canvas>
JS:
// crop button
var button = $("button");
// handle click
button.on("click", function(){
let rect = new fabric.Rect({
left: selectionRect.left,
top: selectionRect.top,
width: selectionRect.getScaledWidth(),
height: selectionRect.getScaledHeight(),
absolutePositioned: true
});
currentImage.clipPath = rect;
canvas.remove(selectionRect);
canvas.renderAll();
});
var canvas = new fabric.Canvas('canvas');
canvas.preserveObjectStacking = true;
var selectionRect;
var currentImage;
fabric.Image.fromURL('https://www.sheffield.ac.uk/polopoly_fs/1.512509!/image/DiamondBasement.jpg', img => {
img.scaleToHeight(500);
img.selectable = true;
canvas.add(img);
canvas.centerObject(img);
currentImage = img;
canvas.backgroundColor = "#333";
addSelectionRect();
canvas.setActiveObject(selectionRect);
canvas.renderAll();
});
function addSelectionRect() {
selectionRect = new fabric.Rect({
fill: 'rgba(0,0,0,0.3)',
originX: 'left',
originY: 'top',
stroke: 'black',
opacity: 1,
width: currentImage.width,
height: currentImage.height,
hasRotatingPoint: false,
transparentCorners: false,
cornerColor: 'white',
cornerStrokeColor: 'black',
borderColor: 'black',
});
selectionRect.scaleToWidth(300);
canvas.centerObject(selectionRect);
selectionRect.visible = true;
canvas.add(selectionRect);
}
Here is my jsfiddle: https://jsfiddle/04nvdeb1/23/
Share Improve this question edited Mar 23, 2020 at 16:14 AlexPavlov asked Mar 23, 2020 at 15:33 AlexPavlovAlexPavlov 4375 silver badges8 bronze badges 3- Has the clipping area to be absolute? or that was something you tried? Do you want the image to be movable pared to the clipping area? – AndreaBogazzi Commented Apr 2, 2020 at 10:31
- @AndreaBogazzi, thank you for looking into this! Clipping area doesn't have to be absolute, I guess I made it absolute to simplify the calculations. Ideally, the clipping area should be "pinned" to the image when the image is moving. Also, I need to have a function to rotate the image, so clipping should work with rotated images as well. – AlexPavlov Commented Apr 10, 2020 at 10:58
- clipping works with rotated images too, is just that you counter rotate the clipping area. – AndreaBogazzi Commented Apr 10, 2020 at 16:26
3 Answers
Reset to default 4I also faced this same issue for a few days. But i could not found any solution even i also found your SO question and github issue. I think clipPath is the best choice for clipping the image using a nice adjustable musk.
Demo Link
Github Repo Link
In the button callback function, you have to implement like this way
step 1: 1st you have to create an Image instance to hold the cropped image
let cropped = new Image();
Step 2: You have to use Canvas.toDataURL() to exports the canvas element to a dataurl image. Here wan want to export only the selection rectangle or mask rectangle, so we pass the mast rect left, top, width, and height
cropped.src = canvas.toDataURL({
left: rect.left,
top: rect.top,
width: rect.width,
height: rect.height,
});
Step 3: I the last step after the image loaded we clear the canvas. create new object with our cropped image, and set some option and rerender the canvas
cropped.onload = function () {
canvas.clear();
image = new fabric.Image(cropped);
image.left = rect.left;
image.top = rect.top;
image.setCoords();
canvas.add(image);
canvas.renderAll();
};
i actually marged Crop Functionality using FabricJs #soution2 with your problem.
Actually you don't have to use clipPath method. What you want is actually move the area you need to the corner, and change the size of canvas.
so the sodo code is as below:
fabricCanvas.setDimensions({
width: cropRectObject.getScaledWidth(),
height: cropRectObject.getScaledHeight()
})
imageObject.set({
left: -cropRectObject.left,
top: -cropRectObject.top
})
fabricCanvas.remove(cropRectObject);
tweaked your fiddle a bit to show this idea: https://jsfiddle/tgy320w4/4/
hope this could help.
const { width, height, left = 0, top = 0 } = rect;
if (width == null || height == null) return;
const zoom = canvas.getZoom();
// toExact is a prototype for precision
const nw = (width * zoom).toExact(1);
const nh = (height * zoom).toExact(1);
const nl = (left * zoom).toExact(1);
const nt = (top * zoom).toExact(1);
// Apply
canvas.clipPath = o;
// Size
canvas.setWidth(nw);
canvas.setHeight(nh);
canvas.absolutePan(new fabric.Point(nl, nt));
canvas.remove(rect);