最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Snap shape to center with FabricJS - Stack Overflow

programmeradmin1浏览0评论

I created a Vertical line in the center of my canva, I'd like my shape to horizontaly align by it's center, right on the line when it es close to it (the user can take it out if he doesn't want to align).

I'm having a problem with my JSFiddle : /

The issue is that when the shape moves it stays block near the center of the line and I cannot get it out anymore.

Here is my FabricJS code :

var canvas = new fabric.Canvas('c', { selection: false });

var line9 = new fabric.Line([
  canvas.width / 2, 0,
  canvas.width / 2, canvas.width
],{
  stroke: 'green',
})

line9.selectable = false;
line9.evented = false;
canvas.add(line9);

canvas.add(new fabric.Circle({ 
  left: 10, 
  top: 100, 
  radius: 50, 
  fill: '#9f9', 
  originX: 'left', 
  originY: 'top',
  centeredRotation: true
}));

canvas.on('object:moving', function(options) {
    if (Math.round(options.target.left) < (canvas.width / 2) - 15 ||
    Math.round(options.target.left) > (canvas.width / 2) + 15) {
      options.target.set({
        left: Math.round(canvas.width / 2),
      }).setCoords();
    }
});

How should I fix my code ?


UPDATE :

canvas.on('object:moving', function(options) {
    if (Math.round(options.target.left) >= (canvas.getActiveObject().getWidth()) - 20 && Math.round(options.target.left) <= (canvas.getActiveObject().getWidth()) + 20) {
      options.target.set({
        left: (canvas.width / 2) - canvas.getActiveObject().getWidth() / 2,
      }).setCoords();
    }
});

This works but when I scale the shape the center point change it's coordination.

I created a Vertical line in the center of my canva, I'd like my shape to horizontaly align by it's center, right on the line when it es close to it (the user can take it out if he doesn't want to align).

I'm having a problem with my JSFiddle : https://jsfiddle/z0u05hee/5/

The issue is that when the shape moves it stays block near the center of the line and I cannot get it out anymore.

Here is my FabricJS code :

var canvas = new fabric.Canvas('c', { selection: false });

var line9 = new fabric.Line([
  canvas.width / 2, 0,
  canvas.width / 2, canvas.width
],{
  stroke: 'green',
})

line9.selectable = false;
line9.evented = false;
canvas.add(line9);

canvas.add(new fabric.Circle({ 
  left: 10, 
  top: 100, 
  radius: 50, 
  fill: '#9f9', 
  originX: 'left', 
  originY: 'top',
  centeredRotation: true
}));

canvas.on('object:moving', function(options) {
    if (Math.round(options.target.left) < (canvas.width / 2) - 15 ||
    Math.round(options.target.left) > (canvas.width / 2) + 15) {
      options.target.set({
        left: Math.round(canvas.width / 2),
      }).setCoords();
    }
});

How should I fix my code ?


UPDATE :

canvas.on('object:moving', function(options) {
    if (Math.round(options.target.left) >= (canvas.getActiveObject().getWidth()) - 20 && Math.round(options.target.left) <= (canvas.getActiveObject().getWidth()) + 20) {
      options.target.set({
        left: (canvas.width / 2) - canvas.getActiveObject().getWidth() / 2,
      }).setCoords();
    }
});

This works but when I scale the shape the center point change it's coordination.

Share Improve this question edited Mar 26, 2017 at 17:57 Horai Nuri asked Mar 26, 2017 at 3:21 Horai NuriHorai Nuri 5,58817 gold badges84 silver badges136 bronze badges 2
  • I'm having trouble understanding what is the behaviour you want. Could you explain a bit more? – jakeehoffmann Commented Mar 26, 2017 at 3:47
  • @jakeehoffmann Yes, I want my round shape to snap horizontally to center whenever it es close to the line. The user can take off the shape from center whenever he wants. – Horai Nuri Commented Mar 26, 2017 at 11:37
Add a ment  | 

4 Answers 4

Reset to default 5

If I understood you correctly, this should do it:

And a small tweak to your code (relevant part):

var snapZone = 15;
canvas.on('object:moving', function(options) {
  var objectMiddle = options.target.left + options.target.getWidth() / 2;
  if (objectMiddle > canvas.width / 2 - snapZone &&
    objectMiddle < canvas.width / 2 + snapZone) {
    options.target.set({
      left: canvas.width / 2 - options.target.getWidth() / 2,
    }).setCoords();
  }
});

And the all important JSFiddle update, https://jsfiddle/rekrah/9k9hd49u/.

Things are a little more plicated when you resize the canvas after you put an object on it. For whatever reason fabric js returns canvas.width in viewport coordinates, i.e. when you resize, width changes. object.width however is NOT affected by a canvas resize so stays the same. The following snippet works for me after resize of canvas and/or resize of objects. If anyone has a simpler solution to acplish this - please share!

// Snapping while moving
canvas.on('object:moving', options => {
  const obj = options.target;

  // snap only if the object is upright
  if (obj.angle !== 0) {
    return;
  }
  
  // Capture area of snap
  let snap = 10;
  // Compensate snap size for transform
  const mySnap = snap / canvas.viewportTransform[0];

  // Sets corner position coordinates based on current angle, width and height
  obj.setCoords();

  // Snap left
  if (Math.abs(obj.oCoords.tl.x) < mySnap) {
    obj.left = 0;
  }
  // Snap right
  else if (Math.abs(obj.aCoords.tr.x - canvas.vptCoords.tr.x) < mySnap) {
    let canvasVptWidth = canvas.vptCoords.tr.x;
    obj.left = canvasVptWidth - obj.getScaledWidth();
  }
  // Snap center horizontally
  else if (Math.abs(obj.oCoords.mt.x - canvas.width / 2) < mySnap) {
    canvasVptWidth = canvas.vptCoords.br.x;
    obj.left = (canvasVptWidth - obj.getScaledWidth()) / 2;
  }

  // Snap top
  if (Math.abs(obj.aCoords.tl.y) < mySnap) {
    obj.top = 0;
  }
  // Snap bottom
  else if (Math.abs(obj.aCoords.br.y - canvas.vptCoords.br.y) < mySnap) {
    let canvasVptHeight = canvas.vptCoords.br.y;
    obj.top = canvasVptHeight - obj.getScaledHeight();
  }
  // Snap center vertically
  else if (Math.abs(obj.oCoords.ml.y - canvas.height / 2) < mySnap) {
    canvasVptHeight = canvas.vptCoords.br.y;
    obj.top = (canvasVptHeight - obj.getScaledHeight()) / 2;
  }
});

I added some improvements to the Tim Harker code. This code solves several problems:

  1. Allows to get vertical and horizontal magnetic centering;
  2. Fixed "getWidth() - not a function" bug;
  3. Fixed centering bug for different objects with setted width or height (in example images from unsplash);
  4. Fixed bug with centering after objects scale;
  5. Added lines hiding feature after mouse up event.

var line9 = new fabric.Line([
    canvas.width / 2, 0,
    canvas.width / 2, canvas.width
], {
    strokeDashArray: [5, 5],
    stroke: 'red',
})

line9.selectable = false;
line9.evented = false;

var line10 = new fabric.Line([
    0, canvas.height / 2,
    canvas.width, canvas.height / 2

], {
    strokeDashArray: [5, 5],
    stroke: 'red',
    strokeWidth: 1,
})

line10.selectable = false;
line10.evented = false;



var snapZone = 15;

canvas.on('object:moving', function(options) {
    var objectMiddleHorizontal = options.target.left + (options.target.width * options.target.scaleX) / 2;

    if (objectMiddleHorizontal > canvas.width / 2 - snapZone &&
        objectMiddleHorizontal < canvas.width / 2 + snapZone) {
        options.target.set({
            left: canvas.width / 2 - (options.target.width * options.target.scaleX) / 2,
        }).setCoords();

        canvas.add(line9);

        document.addEventListener("mouseup", () => {
            canvas.remove(line9);
        });

    } else {
        canvas.remove(line9);
    }

    var objectMiddleVertical = options.target.top + (options.target.height * options.target.scaleY) / 2;

    if (objectMiddleVertical > canvas.height / 2 - snapZone &&
        objectMiddleVertical < canvas.height / 2 + snapZone) {
        options.target.set({
            top: canvas.height / 2 - (options.target.height * options.target.scaleY) / 2,
        }).setCoords();

        canvas.add(line10);

        document.addEventListener("mouseup", () => {
            canvas.remove(line10);
        });

    } else {
        canvas.remove(line10);
    }

});

https://jsfiddle/dmitryjast/wb3jmv9g/2/

Enjoy!

Tims answer is correct but if you rotate object it will not work. i find a way to fix that problem:

  const objectMiddleFromLeft = object.getCenterPoint().x;
  const objectMiddleFromTop = object.getCenterPoint().y;

  object.setPositionByOrigin(
    {
      x:
        objectMiddleFromLeft > canvas.width / 2 - 15 &&
        objectMiddleFromLeft < canvas.width / 2 + 15
          ? canvas.width / 2
          : objectMiddleFromLeft,
      y:
        objectMiddleFromTop > canvas.height / 2 - 15 &&
        objectMiddleFromTop < canvas.height / 2 + 15
          ? canvas.height / 2
          : objectMiddleFromTop,
    },
    "center",
    "center"
  );
发布评论

评论列表(0)

  1. 暂无评论