4

I've implemented canvas panning using a Fabric JS canvas, using the below code:

canvas.on("mouse:down", function(e) {
  panning = true;
});

canvas.on("mouse:up", function(e) {
  panning = false;
});

canvas.on("mouse:move", function(e) {
  if (panning) {
    var delta = new fabric.Point(e.e.movementX, e.e.movementY);
    canvas.relativePan(delta);
  }
});

This works fine but you can scroll/pan infinitely in any direction. I want to limit this to a boundary so that a smaller canvas is effectively a view on a larger drawing area. For example a 400 X 400 pixel canvas, which doesn't allow you to pan around more than say 1000 X 1000 pixel area. I've seen in Fabric JS canvas object there's a viewportTransform[] array, which holds zoom level in field [0] and X and Y offsets in fields [4] and [5] but not sure how best to implement a panning boundary. Are there Fabric functions that would make this work?

I also have to take account of zoom level (I'm using canvas.setZoom()) and don't want a user dragging objects beyond the panning boundary either (this may be a separate problem!).

Any thoughts?

Thanks!

n1stre
  • 5,856
  • 4
  • 20
  • 41
Alex Kerr
  • 956
  • 15
  • 44

1 Answers1

2

there's already a tutorial about it in FabricJS webpage, it works with mousewheel, but you can adapt it: http://fabricjs.com/fabric-intro-part-5

I did it with the some buttons.

First of all I have to tell you that zoom and panning affects canvas viewportTransform property, which is a matrix, similar to css transform property works (or I believe so...).

These are some outputs I got in console while I was working:

originalViewporTransform (6) [1, 0, 0, 1, 0, 0]

zoom Affects index 0 & 3

zoomViewporTransform (6) [1.2, 0, 0, 1.2, 0, 0] zoomViewporTransform (6) [1.44, 0, 0, 1.44, 0, 0]

panning

Right: Affects index 4

panningViewporTransform (6) [1.44, 0, 0, 1.44, -82, 0] panningViewporTransform (6) [1.44, 0, 0, 1.44, -83, 0]

Left: Affects index 4

panningViewporTransform (6) [1.728, 0, 0, 1.728, 259, 0] panningViewporTransform (6) [1.728, 0, 0, 1.728, 260, 0]

Bottom: Affects index 5 panningViewporTransform (6) [1.728, 0, 0, 1.728, 0, -241] panningViewporTransform (6) [1.728, 0, 0, 1.728, 0, -242]

Top: Affects index 5 panningViewporTransform (6) [1.728, 0, 0, 1.728, 0, 305] panningViewporTransform (6) [1.728, 0, 0, 1.728, 0, 306]

And here's my function to control panning with buttons, in four directions. To limit top and left panning you just have to set the respective viewport to 0 whenever is bigger. When is about right and bottom panning, you have to put the number size of the boundaries you are considering. I was considering the size of my canvas, but you can use which ever size you want.

...
else if (this.actualCanvasViewportTransform[4] < this.canvas.getWidth() - (this.canvas.getWidth() * this.actualCanvasZoom))
...

at those lines you would have to change (this.canvas.getWidth() * this.actualCanvasZoom) with the size you want, like so

(pxLimitRight * this.actualCanvasZoom)

hope it helps

whileMouseDown(caseType){

   if (this.actualCanvasZoom <= this.originalCanvasZoom) return;

   const units = 1;
   let   delta;

   switch (caseType) {
      case 'right':
        delta = new fabric.Point(-units,0);
        break;
      case 'left':
        delta = new fabric.Point(units,0);
        break;
      case 'bottom':
        delta = new fabric.Point(0,-units);
        break;
      case 'top':
        delta = new fabric.Point(0,units);
        break;
    }

this.canvas.relativePan(delta);

// console.log('panningViewporTransform', this.canvas.viewportTransform, this.actualCanvasZoom);

    this.actualCanvasViewportTransform = this.canvas.viewportTransform;

    /*
    WE ARE PANNING LEFT AND RIGHT
    */
    if (this.actualCanvasViewportTransform[4] >= 0) {
      // WE ARE GOING LEFT
      this.actualCanvasViewportTransform[4] = 0;

    } else if (this.actualCanvasViewportTransform[4] < this.canvas.getWidth() - (this.canvas.getWidth() * this.actualCanvasZoom)) 
    {
      // WE ARE GOING RIGHT
      this.actualCanvasViewportTransform[4] = this.canvas.getWidth() - (this.canvas.getWidth() * this.actualCanvasZoom);

    }


    /*
    WE ARE PANNING DOWN AND UP
    */
    if (this.actualCanvasViewportTransform[5] >= 0) {
      // WE ARE GOING UP
      this.actualCanvasViewportTransform[5] = 0;

    } else if (this.actualCanvasViewportTransform[5] < this.canvas.getHeight() - (this.canvas.getHeight() * this.actualCanvasZoom)) {
      // WE ARE GOING DOWN
      this.actualCanvasViewportTransform[5] = this.canvas.getHeight() - (this.canvas.getHeight() * this.actualCanvasZoom);

    }

 }
Chema
  • 887
  • 8
  • 14