18

i am new in fabric js want to set the drag limitenter image description here

i have also try with https://github.com/kangax/fabric.js/wiki/Working-with-events

not able to get the solution.

please check the attached image, object can move anyware but it should be display in red area only.i want this. help me...thanks in advance !!

1
  • I am using fabric js and I only need to display the bounding box of an Object, not the whole object during dragging. Means to say Object would be fixed until I stops the dragging. Commented Nov 15, 2013 at 7:51

5 Answers 5

15

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

5
  • 1
    This is almost exactly what I am looking for - except I need the image to slide within a boundary larger than the canvas. Your solution provides this ability by simply making the boundingBox the necessary size. Great stuff. Commented Oct 22, 2014 at 4:33
  • initial implementation works great! Just need to tweak it for my specific instance.
    – hellatan
    Commented Jan 2, 2015 at 16:26
  • 1
    I had to either set the color of movingBox or make boundingBox.fill "transparent" for it to show up in your demo; otherwise I had no idea what was going on.
    – drzaus
    Commented Jan 15, 2015 at 16:24
  • there are many things this code doesn't take into account; I've posted an expanded version which takes into account some of those things. Commented Jan 20, 2015 at 5:20
  • Great example in JSFiddle. Works great with fabric.js v1.5.0. Awesome :) Thanks :) Commented Sep 17, 2015 at 9:22
15

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));          
  }
});
2
  • Good answer cut n' paste and it works like a charm :-)
    – rtn
    Commented May 5, 2015 at 11:20
  • 5
    Works perfectly. In v. 2.0 the setLeft(x) and setTop(y) needs to be changed to set('left',x) and set('top',y).
    – Flemming
    Commented Feb 25, 2018 at 9:00
8

What had worked for me is to create an event listener for the object:moving event. When the move is happening you update the goodtop and goodleft variables and once you are out of bounds to reposition the object to the last good points.

var goodtop, goodleft, boundingObject;

canvas.on("object:moving", function(){
    var obj = this.relatedTarget;
    var bounds = boundingObject;
    obj.setCoords();
    if(!obj.isContainedWithinObject(bounds)){
        obj.setTop(goodtop);
        obj.setLeft(goodleft);
        canvas.refresh();    
    } else {
        goodtop = obj.top;
        goodleft = obj.left;
    }  
});
3
  • thanks for reply can you please give more infor or any example Commented Nov 15, 2013 at 7:06
  • I am using fabric js and I only need to display the bounding box of an Object, not the whole object during dragging. Means to say Object would be fixed until I stops the dragging. Commented Nov 15, 2013 at 7:50
  • Good idea! I tried to use this idea in my case. it works well.
    – Calvin
    Commented Feb 25, 2016 at 4:48
1

I used Michael Johnston's snipped as a starting point to add bounding control for rotated elements. This snipped only covers the cases when either (obj.centerX && obj.centerY == "center") || (obj.centerX && obj.centerY != "center")

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

      // get left, top, width, and height of object
      var left = obj.left;
      var top = obj.top;
      var width = obj.width * obj.scaleX;
      var height = obj.height * obj.scaleY;

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

      // calculate values that define the origin of the object, when it is centered in the center or not
      var left_adjust, right_adjust;
      if (obj.originX == "center") {
        left_adjust = right_adjust = width / 2;
      } else {
        left_adjust = 0;
        right_adjust = width;
      }
      var top_adjust, bottom_adjust;
      if (obj.originY == "center") {
        top_adjust = bottom_adjust = height / 2;
      } else {
        top_adjust = 0;
        bottom_adjust = height;
      }

      // support for rotated objects
      if (obj.angle) {
        var angle = obj.angle;
        if (angle > 270) {
          angle -= 270;
        } else if (angle > 180) {
          angle -= 180;
        } else if (angle > 90) {
          angle -= 90;
        }
        const radians = angle * (Math.PI / 180);
        const w_opposite = width * Math.sin(radians);
        const w_adjacent = width * Math.cos(radians);
        const h_opposite = height * Math.sin(radians);
        const h_adjacent = height * Math.cos(radians);

        if (obj.originX != "center" && obj.originY != "center") {
          if (obj.angle <= 90) {
            left_adjust = h_opposite;
            top_adjust = 0;
            right_adjust = w_adjacent;
            bottom_adjust = h_adjacent + w_opposite;
          } else if (obj.angle > 90 && obj.angle <= 180) {
            left_adjust = h_adjacent + w_opposite;
            top_adjust = h_opposite;
            right_adjust = 0;
            bottom_adjust = w_adjacent;
          } else if (obj.angle > 180 && obj.angle <= 270) {
            left_adjust = w_adjacent;
            top_adjust = w_opposite + h_adjacent;
            right_adjust = h_opposite;
            bottom_adjust = 0;
          } else {
            left_adjust = 0;
            top_adjust = w_adjacent;
            right_adjust = w_opposite + h_adjacent;
            bottom_adjust = h_opposite;
          }
        }

        if (obj.originX == "center" && obj.originY == "center") {
          if (obj.angle <= 90 || (obj.angle > 180 && obj.angle <= 270)) {
            left_adjust = (w_adjacent + h_opposite) / 2;
            right_adjust = (w_adjacent + h_opposite) / 2;
            top_adjust = (h_adjacent + w_opposite) / 2;
            bottom_adjust = (h_adjacent + w_opposite) / 2;
          } else {
            left_adjust = (h_adjacent + w_opposite) / 2;
            right_adjust = (h_adjacent + w_opposite) / 2;
            top_adjust = (w_adjacent + h_opposite) / 2;
            bottom_adjust = (w_adjacent + h_opposite) / 2;
          }
        }
      }

      // 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 (width > c_width) {
        obj.set("left", left_bound);
      } else {
        obj.set("left", Math.min(Math.max(left, left_bound), right_bound));
      }

      if (height > c_height) {
        obj.set("top", top_bound);
      } else {
        obj.set("top", Math.min(Math.max(top, top_bound), bottom_bound));
      }
});
0
-7

SEE THE WORKING EXAMPLE It's as simple as a water try this

just use this js

<script type="text/javascript">
   //use global variable for canvas object


  var canvas;
  var ctx;
 function onLoad() {
  //get fabric canvas with id mycanvas


   canvas = new fabric.Canvas('mycanvas');


   canvas.on("mouse : down",function{

   //get canvas 2d context
   ctx = canvas.getContext("2d");

   ctx.beginPath();
   ctx.rect(115,60,221,390);//specify bounded rectangle
   ctx.closePath();
   ctx.clip();
   ctx.save();
  });
    //now restore the context on mouse up

    canvas.on("mouse : up",function(){
         ctx.restore();//restore the context
    });
}



 </script>

Hope this will help you. Njoy coding :) now u can have the same clipping region for every object supported by Fabric.js for transformation and moving. try it :).

1
  • 2
    this is not even close to answering the upper question Commented Sep 30, 2015 at 10:23

Not the answer you're looking for? Browse other questions tagged or ask your own question.