Ralitza
Ralitza

Reputation: 11

Fabric js transform and zoom canvas to fit all objects in viewport

I'm trying to made a collaboration tool based on fabricJs. Because I want canvas to be responsive I get some problems with pan/zoom functionality

Canvas view on large monitor

Problem is when someone on large monitor put some objects on corners for example and another user open this canvas on laptop or small monitor. In this case there is objects outside of current user viewport

Canvas view on small monitor

My idea is to add a button and functionality to fit all objects inside view with zoom and viewportTransform (I don't want to move objects in center of canvas because after save I will get different results from origin).

For this first I create a group with

let group = new fabric.Group(_this.canvas.getObjects()); and then I calculate zoom with canvas.height / group.height and I look zoom to be calculated properly.

After that I don't know how to calculate where I need to zoom canvas. I try with getBoundingRect, calcViewportBoundaries and other function but can`t get correct zoom point.

JSFiddle - https://jsfiddle.net/znru0yx6/4/

Upvotes: 0

Views: 2150

Answers (1)

Ryk Waters
Ryk Waters

Reputation: 677

I know this is 2.5 years after you asked. But this might help others who stumble across this (as i did) and give you some closure as well.

I've tried to include as many comments as possible as i always prefer to see the explanation alongside each line, rather than separately.

window.zoomToContents = function(canvas){
  //first check if there are any elemnts to zoom to
  if (canvas.getObjects().length < 1) {
    return;
  }
  // reset zoom so pan actions work as expected
  canvas.setZoom(1);
  //group all the objects
  const group = new fabric.Group(canvas.getObjects());
  //find the centre of the group on the canvas
  const x = (group.left + (group.width / 2)) - (canvas.width / 2);
  const y = (group.top + (group.height / 2)) - (canvas.height / 2);
  //and pan to it
  canvas.absolutePan({x:x, y:y});
  //now we need to decide whether width or height should determine the scaling
  //e.g. a portrait box in a landscape canvas (height) needs scaling differently to a portrait box in a portrait canvas (could be height or width)
  //or a landscape box in a portrait canvas (width)
  //work out the distance between the edges of the group and the canvas
  const heightDist = canvas.getHeight() - group.height;
  const widthDist = canvas.getWidth() - group.width;
  let groupDimension = 0;
  let canvasDimension = 0;
  //the smaller the number then that's the side we need to use as a reference to scale
  //either group is inside the canvas (positive number) so the edge closest to the limits of the canvas will be used as the reference scale (smallest positive difference)
  //or the group extends outside the canvas so the edge that extends further will be the reference (large negative number)
  //either way, we want the smallest number
  if (heightDist < widthDist) {
    //height is the reference so need the height to scale to be nearly the height of the canvas
    groupDimension = group.height;
    canvasDimension = canvas.getHeight();
  } else {
    //width is the reference so need the width to scale to be nearly the width of the canvas
    groupDimension = group.width;
    canvasDimension = canvas.getWidth();
  }
  //work out how to scale the group to match the canvas size (then only make it zoom 80% of the way)
  const zoom = (canvasDimension / groupDimension) * 0.8;
  //we've already panned the canvas to the centre of the group, so now zomm using teh centre of teh canvas as teh reference point
  canvas.zoomToPoint({ x: canvas.width / 2, y: canvas.height / 2 }, zoom);
}

Upvotes: 3

Related Questions