Set object drag limit in Fabric.js

While Orangepill's answer is correct, it produces a "stuttering" when your object hits the object bounds. If you have a rectangular bounding box (and not a complex bounding object) an alternative is to allow the object to be dragged along the bounds and "slide" along the bounding box. You do this by capping the coordinates values and letting the other dimension move as usual. An example snippet would look like so:

var canvas = new fabric.Canvas("bounded");

var boundingBox = new fabric.Rect({
  fill: "none",
  width: 600,
  height: 400,
  hasBorders: false,
  hasControls: false,
  lockMovementX: true,
  lockMovementY: true,
  evented: false,
  stroke: "red"
});

var movingBox = new fabric.Rect({
  width: 100,
  height: 100,
  hasBorders: false,
  hasControls: false
});

canvas.on("object:moving", function() {
  var top = movingBox.top;
  var bottom = top + movingBox.height;
  var left = movingBox.left;
  var right = left + movingBox.width;

  var topBound = boundingBox.top;
  var bottomBound = topBound + boundingBox.height;
  var leftBound = boundingBox.left;
  var rightBound = leftBound + boundingBox.width;

  // capping logic here
  movingBox.setLeft(Math.min(Math.max(left, leftBound), rightBound - movingBox.width));
  movingBox.setTop(Math.min(Math.max(top, topBound), bottomBound - movingBox.height));
});

canvas.add(boundingBox);
canvas.add(movingBox);

See this example in JSFiddle here


Felix Fung's answer was a starting point, but there are many things to consider. Here is a version that accounts for some of them.

It handles the canvas having a viewport transform (ie, zoomed/panned) and objects that are center-origined instead of left/top-origined. It also constrains objects wider/taller than the viewport to the top/left instead of the bottom/right.

canvas.on("object:moving", function(e) {
  var obj = e.target;
  var canvas = obj.canvas;
  var top = obj.top;
  var left = obj.left;
  var zoom = canvas.getZoom();
  var pan_x = canvas.viewportTransform[4];
  var pan_y = canvas.viewportTransform[5];

  // width & height we are constraining to must be calculated by applying the inverse of the current viewportTransform
  var c_width = canvas.width / zoom;
  var c_height = canvas.height / zoom;


  var w = obj.width * obj.scaleX
  var left_adjust, right_adjust
  if(obj.originX == "center") {
    left_adjust = right_adjust = w / 2;
  } else {
    left_adjust = 0;
    right_adjust = w;
  }

  var h = obj.height * obj.scaleY;
  var top_adjust, bottom_adjust;
  if(obj.originY == "center") {
    top_adjust = bottom_adjust = h / 2;
  } else {
    top_adjust = 0;
    bottom_adjust = h;
  }

  // if you need margins set them here
  var top_margin = 0;
  var bottom_margin = 0;
  var left_margin = 0;
  var right_margin = 0;


  var top_bound = top_margin + top_adjust - pan_y;
  var bottom_bound = c_height - bottom_adjust - bottom_margin - pan_y;
  var left_bound = left_margin + left_adjust - pan_x;
  var right_bound = c_width - right_adjust - right_margin - pan_x;

  if( w > c_width ) {
    obj.setLeft(left_bound);
  } else {
    obj.setLeft(Math.min(Math.max(left, left_bound), right_bound));          
  }

  if( h > c_height ) {
    obj.setTop(top_bound);
  } else {
    obj.setTop(Math.min(Math.max(top, top_bound), bottom_bound));          
  }
});

Tags:

Fabricjs