Centering and scaling canvas object inside another canvas object by width/height in Fabric.js
Asked Answered
I

1

1

Goal: To center an object (horizontally and vertically) inside another object (rectangle or group) on canvas via Fabric.js or via Javascript which keeps the original object aspect ratio the same, but also not exceed the parent object width/height proportions?

The parent object (rectangle or group) won't be centered on the canvas element.

Here's the code I have so far: https://stackblitz.com/edit/angular-my8hky

My app.component.ts so far:

canvas: fabric.Canvas;

ngOnInit() {
  this.canvas = new fabric.Canvas('canvas');
  var rect = new fabric.Rect({
    left: 100,
    top: 50,
    width: 300,
    height: 200,
    fill: '#eee'
  });
  this.canvas.add(rect);

  fabric.Image.fromURL('https://angular.io/assets/images/logos/angular/[email protected]', (img) => {
    let bounds = rect.getBoundingRect();
    let oImg = img.set({
      left: bounds.left,
      top: bounds.top,
      width: rect.width
    }).scale(1);
    this.canvas.add(oImg).renderAll();
  });
}

Not only is the new object not centered vertically, but also not centered horizontally if the rectangle object height is decreased (for example to 50px in height).

I realize I'm only declaring the inner image object width to be the same as the parent rectangle boundary width.

Current solution:

Parent rectangle width: 300 and height: 200:

300px x 200px - Undesired Fabric.js centering solution

Parent rectangle width: 300 and height: 50:

300px x 50px - Undesired Fabric.js centering solution

Desired solution:

Parent rectangle width: 300 and height: 200:

300px x 200px - Desired Fabric.js centering solution

Parent rectangle width: 300 and height: 50:

300px x 50px - Desired Fabric.js centering solution

Can anyone assist?

Instable answered 5/11, 2019 at 21:27 Comment(0)
I
0

Figured it out.. Here's the StackBlitz: https://stackblitz.com/edit/angular-pg4fis

The trick was to first add the rectangle to a Fabric group, like so:

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);

Then, I was able to establish the parent (group) element boundaries and use a variable scaling factor to set the image via Math functionality:

fabric.Image.fromURL('https://angular.io/assets/images/logos/angular/[email protected]', (img) => {
  let bounds = rectGroup.getBoundingRect();

  const scaleFactor = Math.min(
    Math.min(1, bounds.width / img.width),
    Math.min(1, 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();
});
Instable answered 6/11, 2019 at 20:25 Comment(4)
You can do like this as well, add image object on the center of rectangle object,Pl
@Pl Thank you, this is a great solution for if you want the proportions to be kept for the image and not resized. I changed the height on the rectangle to something like 50px and the image was leaking out of the box.Instable
did change for position, you can take your code for scalePl
@Pl - any chance you could help me with a similar question: #58826790Instable

© 2022 - 2024 — McMap. All rights reserved.