FabricJS: How to auto-resize image to fit group or rectangle object? - html5-canvas

Goal: I'm seeking to add an image to a FabricJS group or rectangle object, and for the image to maintain it's original aspect ratio and center fit it's parent width/height.
I don't want the image overflow to show, but will show the overflow as a less opaque green in the examples below:
Landscape Image Example:
If it was a landscape oriented image, it would scale to max height, but then center itself with regards to width placement:
Since I'm not looking for the image overflow to show, the final product should look like this:
Portrait Image Example:
Similarly, if the image was portrait oriented, the image would scale 100% in width, but center itself in height:
And then negating the overflow, this is what I'm looking for as a final product:
Here's my stackblitz so far: https://stackblitz.com/edit/angular-gpfkkw
With the base-case code as follows:
this.canvas = new fabric.Canvas('canvas');
var rect = new fabric.Rect({
left: 100,
top: 50,
width: 450,
height: 200,
fill: '#e3e3e3',
});
var rectGroup = new fabric.Group([rect], {
name: 'Rectangle',
});
this.canvas.add(rectGroup);
fabric.Image.fromURL('https://placehold.it/888x500&text=16:9', (img) => {
let bounds = rectGroup.getBoundingRect();
const scaleFactor = Math.min(
Math.min(bounds.width / img.width),
Math.min(bounds.height / img.height)
);
img.scale(scaleFactor);
img.set({
top: bounds.top + Math.max(bounds.height - img.height * scaleFactor, 0)/2,
left: bounds.left + Math.max(bounds.width - img.width * scaleFactor, 0)/2,
});
rectGroup.addWithUpdate(img);
this.canvas.renderAll();
}
This is obviously not the right solution, but a start. Any advice?

I figured it out, here's the Stackblitz: https://stackblitz.com/edit/angular-yoo8h5
First, I compared whether or not the rectangle aspect ratio is proportional to the image through an if/else statement which decides whether or not to initially scale the image to the rectangle width/height.
Then I set either the top or the left attribute of the image to the rectangle boundary point. Then calculating the center point of the rectangle and subtracting half of the image's boundary and lastly, setting the rectangle group object's clipPath to that of the rectangle, it worked!
fabric.Image.fromURL('https://placehold.it/888x500&text=16:9', (img) => {
let bounds = rectGroup.getBoundingRect();
if ((bounds.height / bounds.width) >= (img.height / img.width)) {
img.scaleToHeight(bounds.height);
img.set({
top: bounds.top,
left: (bounds.left + (bounds.width/2)) - (img.getBoundingRect().width/2)
});
}
else {
img.scaleToWidth(bounds.width);
img.set({
top: (bounds.top + (bounds.height/2)) - (img.getBoundingRect().height/2),
left: bounds.left
});
}
rectGroup.addWithUpdate(img);
rectGroup.clipPath = rect;
this.canvas.renderAll();
});

A bit late here but chipping in in case this might be helpful.
You were heading in the right direction with Math.min, but you can use Math.max instead for the side with the larger group/image ratio to scale to the full length of the side. You can then set the x y origins to center.
fabric.Image.fromURL('https://placehold.it/888x500&text=16:9', (img) => {
let bounds = rectGroup.getBoundingRect();
const scaleFactor = Math.max(bounds.width / img.width, bounds.height / img.height);
img.set({
originX: 'center',
originY: 'center',
scaleX: scaleFactor,
scaleY: scaleFactor
});
rectGroup.addWithUpdate(img);
this.canvas.renderAll();
}

Related

Manually calculate font size for drawn NSAttributedString based on container width

UILabel has the property adjustsFontSizeToFitWidth which calculates the perfect font size if the bounding box is smaller than what text can be displayed at the current font size.
I am drawing text on a UIGraphicsImageRendererContext which is then exported to an image. I would like to recreate the behavior of adjustsFontSizeToFitWidth with the NSAttributedStrings I am currently using. This is the code I use to draw:
let text = NSAttributedString(string: "Example Text", attributes: [NSAttributedString.Key.font: font.withSize(16)])
text.draw(in: CGRect(x: 100, y: 100, width: 100, height: 20))
Is there a way to calculate the perfect font size based on the available width? I assume this would be an algorithm?
I achieved this by finding the scale factor by diving the smaller container width with the proposed string width, then using that scale factor to determine a new font size.
var title = NSAttributedString(string: "Example", attributes: [NSAttributedString.Key.font: font.withSize(fontSize)])
let titleWidth = title.size().width
if titleWidth > containerWidth {
let scaleFactor = containerWidth / titleWidth
title = NSAttributedString(string: title.string, attributes: [NSAttributedString.Key.font: font.withSize(fontSize * scaleFactor)])
}
title.draw(in: CGRect(x: 100, y: 100, width: containerWidth, height: containerHeight))

How to draw an inverted element on a layer in KonvaJS

is there any way to add a shape in KonvaJS which draws outside of it's boundries? Basically a "hole" in the layer, so that I can highlight a spot in the canvas by making everything around a circle darker (black with low opacity).
Any help is much appreciated!
Cheers
Edit:
Here is an image of what I mean (I'm not allowed to embed it yet):
https://i.imgur.com/gvTqgN0.jpg
I was hoping there might be something like this:
Konva.Circle({
x: 100,
y: 100,
radius: 50,
opacity: 0.3,
fill: 'black',
inverted: true
})
and this would then in turn "not draw" a circle, but everything around it would then take the given attributes. In this case it would all be darkend a bit besides the circle.
You can do this with a custom shape:
const shape = new Konva.Shape({
width: stage.width(),
height: stage.height(),
// what is color of background ?
fill: 'rgba(0,0,0,0.2)',
sceneFunc: (ctx, shape) => {
// draw background
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(shape.width(), 0);
ctx.lineTo(shape.width(), shape.height());
ctx.lineTo(0, shape.height());
ctx.lineTo(0, 0);
// now cut circle from the background
// we can do this by useing arc
const x = 200;
const y = 200;
const radius = 10;
// but it must be counter-clockwise
// so the last argument true is very important here
ctx.arc(200, 200, 100, 0, Math.PI * 2, true);
ctx.fillStrokeShape(shape);
},
// remove shape from hit graph, so it is not visible for events
listening: false
});
Demo: https://jsbin.com/tevejujafi/3/edit?html,js,output

Cannot get context.rotate to show image properly

I need to rotate the image 180 degrees, but when I add in the rotate code, the image becomes a square.
var moulding_matte_canvas_height = [],
canvas = document.createElement('canvas'),
ctx = canvas.getContext("2d"),
width = 5.171363636363637,
opening_i = 0,
rotate = 180 * Math.PI / 180,
i = 2;
moulding_matte_canvas_height[0] = 225;
canvas.width = 285;
canvas.height = 335;
img = new Image();
img.src = 'http://www.asa.tframes.org:1881/system/components/compimg/80ac89fad42cf14561b641df241bf406/pattern';
function moulding_get_segment_width(img_width, opening_i)
{
return 175;
}
//
$(img).on("load", function() {
ctx.save();
ctx.translate(width, moulding_matte_canvas_height[opening_i]);
//ctx.rotate(rotate);
//ctx.rotate(Math.PI);
ctx.drawImage(img, 0, 0, moulding_get_segment_width(img.width, i), width);
ctx.restore();
//
});
$("#mattes").append(canvas); /*appending the canvas*/
http://fiddle.jshell.net/onpa628e/1/
(Whitespace is intentional due to other elements that will be drawn and the red next to the image is just for clarity)
Without rotate:
With rotate:
Expected:
Your image looks like a square, because it's only showing a tiny portion on the canvas. This happens because translate is only moving ~ 5px (your "width" variable) on the x axis.
Here's your example modified with a higher x value.

dropzone - change the thumbnail size

I want to upload one file and set the width and height to fit in a given div, depending on the file size. e.g.:
div size: width: 600px, height: 300px
img size: width: 1200px, height: 400px
the thumb gets created with width 600 and height 300 (by using thumbnailWidth and thumbnailHeight options)
on success I receive the filesize. In the example case I let the width of the image and div at 600px, but I want to change the height to 200 (So that the image doesn't get deformed)
My problem now is that the image src (data) still has a width of 300px
Any ideas how to fix that? THANKS!
Change the resize fonction like this:
https://github.com/enyo/dropzone/issues/236
,
resize: function(file) {
var info;
// drawImage(image, srcX, srcY, srcWidth, srcHeight, trgX, trgY, trgWidth, trgHeight) takes an image, clips it to
// the rectangle (srcX, srcY, srcWidth, srcHeight), scales it to dimensions (trgWidth, trgHeight), and draws it
// on the canvas at coordinates (trgX, trgY).
info = {
srcX:0,
srcY:0,
srcWidth: file.width,
srcHeight: file.height,
trgX:0,
trgY:0,
trgWidth: this.options.thumbnailWidth,
trgHeight: parseInt(this.options.thumbnailWidth * file.height / file.width)
}
return info;
},

Kinetic.Image config cornerRadius doesn't work

I currently used KineticJS (version:4.2.0) to do something, but when I want to set corner radius for a kinetic image object, it seems it doesn't work at all. Do anyone have any ideas?
My code is like this:
var stage = new Kinetic.Stage({
container: 'container',
width: 901,
height: 532,
});
var bg = new Kinetic.Layer(); // slot's shell
var bgObj = new Image();
bgObj.onload = function() {
var item = new Kinetic.Image({
x: 0,
y: 0,
width: 901,
height: 532,
image: bgObj,
cornerRadius: 25
});
bg.add(item);
stage.add(bg);
};
bgObj.src = "background.png";
Thanks.
corner radius currently isn't implemented for Image. In fact there's an error with the documentation because only Rectangle supports rounded corners. Images will support rounded corners soon.

Resources