Fabricjs在调整大小时捕捉到网格

时间:2017-05-24 02:04:18

标签: javascript jquery fabricjs

我在尝试根据网格大小调整对象大小时遇到​​了一些问题。

这是我的小提琴:http://jsfiddle.net/csh6c6fw/1/

这是我申请的代码:

canvas.on('object:scaling', (options) => {
  var newWidth = (Math.round(options.target.getWidth() / grid)) * grid;
  var newHeight = (Math.round(options.target.getHeight() / grid)) * grid;

  if (options.target.getWidth() !== newWidth) {
    options.target.set({ width: newWidth, height: newHeight });
  }

});

预期结果

它应该像运动那样对齐网格。

4 个答案:

答案 0 :(得分:5)

可能看起来很复杂但是,以下内容将完成工作:

canvas.on('object:scaling', options => {
   var target = options.target,
      w = target.width * target.scaleX,
      h = target.height * target.scaleY,
      snap = { // Closest snapping points
         top: Math.round(target.top / grid) * grid,
         left: Math.round(target.left / grid) * grid,
         bottom: Math.round((target.top + h) / grid) * grid,
         right: Math.round((target.left + w) / grid) * grid
      },
      threshold = grid,
      dist = { // Distance from snapping points
         top: Math.abs(snap.top - target.top),
         left: Math.abs(snap.left - target.left),
         bottom: Math.abs(snap.bottom - target.top - h),
         right: Math.abs(snap.right - target.left - w)
      },
      attrs = {
         scaleX: target.scaleX,
         scaleY: target.scaleY,
         top: target.top,
         left: target.left
      };
   switch (target.__corner) {
      case 'tl':
         if (dist.left < dist.top && dist.left < threshold) {
            attrs.scaleX = (w - (snap.left - target.left)) / target.width;
            attrs.scaleY = (attrs.scaleX / target.scaleX) * target.scaleY;
            attrs.top = target.top + (h - target.height * attrs.scaleY);
            attrs.left = snap.left;
         } else if (dist.top < threshold) {
            attrs.scaleY = (h - (snap.top - target.top)) / target.height;
            attrs.scaleX = (attrs.scaleY / target.scaleY) * target.scaleX;
            attrs.left = attrs.left + (w - target.width * attrs.scaleX);
            attrs.top = snap.top;
         }
         break;
      case 'mt':
         if (dist.top < threshold) {
            attrs.scaleY = (h - (snap.top - target.top)) / target.height;
            attrs.top = snap.top;
         }
         break;
      case 'tr':
         if (dist.right < dist.top && dist.right < threshold) {
            attrs.scaleX = (snap.right - target.left) / target.width;
            attrs.scaleY = (attrs.scaleX / target.scaleX) * target.scaleY;
            attrs.top = target.top + (h - target.height * attrs.scaleY);
         } else if (dist.top < threshold) {
            attrs.scaleY = (h - (snap.top - target.top)) / target.height;
            attrs.scaleX = (attrs.scaleY / target.scaleY) * target.scaleX;
            attrs.top = snap.top;
         }
         break;
      case 'ml':
         if (dist.left < threshold) {
            attrs.scaleX = (w - (snap.left - target.left)) / target.width;
            attrs.left = snap.left;
         }
         break;
      case 'mr':
         if (dist.right < threshold) attrs.scaleX = (snap.right - target.left) / target.width;
         break;
      case 'bl':
         if (dist.left < dist.bottom && dist.left < threshold) {
            attrs.scaleX = (w - (snap.left - target.left)) / target.width;
            attrs.scaleY = (attrs.scaleX / target.scaleX) * target.scaleY;
            attrs.left = snap.left;
         } else if (dist.bottom < threshold) {
            attrs.scaleY = (snap.bottom - target.top) / target.height;
            attrs.scaleX = (attrs.scaleY / target.scaleY) * target.scaleX;
            attrs.left = attrs.left + (w - target.width * attrs.scaleX);
         }
         break;
      case 'mb':
         if (dist.bottom < threshold) attrs.scaleY = (snap.bottom - target.top) / target.height;
         break;
      case 'br':
         if (dist.right < dist.bottom && dist.right < threshold) {
            attrs.scaleX = (snap.right - target.left) / target.width;
            attrs.scaleY = (attrs.scaleX / target.scaleX) * target.scaleY;
         } else if (dist.bottom < threshold) {
            attrs.scaleY = (snap.bottom - target.top) / target.height;
            attrs.scaleX = (attrs.scaleY / target.scaleY) * target.scaleX;
         }
         break;
   }
   target.set(attrs);
});

这是一个有效的例子:

&#13;
&#13;
var canvas = new fabric.Canvas('c', {
   selection: false
});
var grid = 50;

// create grid
for (var i = 0; i < (600 / grid); i++) {
   canvas.add(new fabric.Line([i * grid, 0, i * grid, 600], {
      stroke: '#ccc',
      selectable: false
   }));
   canvas.add(new fabric.Line([0, i * grid, 600, i * grid], {
      stroke: '#ccc',
      selectable: false
   }))
}

// add objects
canvas.add(new fabric.Rect({
   left: 100,
   top: 100,
   width: 50,
   height: 50,
   fill: '#faa',
   originX: 'left',
   originY: 'top',
   centeredRotation: true
}));

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

// snap to grid
canvas.on('object:moving', options => {
   options.target.set({
      left: Math.round(options.target.left / grid) * grid,
      top: Math.round(options.target.top / grid) * grid
   });
});

canvas.on('object:scaling', options => {
   var target = options.target,
      w = target.width * target.scaleX,
      h = target.height * target.scaleY,
      snap = { // Closest snapping points
         top: Math.round(target.top / grid) * grid,
         left: Math.round(target.left / grid) * grid,
         bottom: Math.round((target.top + h) / grid) * grid,
         right: Math.round((target.left + w) / grid) * grid
      },
      threshold = grid,
      dist = { // Distance from snapping points
         top: Math.abs(snap.top - target.top),
         left: Math.abs(snap.left - target.left),
         bottom: Math.abs(snap.bottom - target.top - h),
         right: Math.abs(snap.right - target.left - w)
      },
      attrs = {
         scaleX: target.scaleX,
         scaleY: target.scaleY,
         top: target.top,
         left: target.left
      };
   switch (target.__corner) {
      case 'tl':
         if (dist.left < dist.top && dist.left < threshold) {
            attrs.scaleX = (w - (snap.left - target.left)) / target.width;
            attrs.scaleY = (attrs.scaleX / target.scaleX) * target.scaleY;
            attrs.top = target.top + (h - target.height * attrs.scaleY);
            attrs.left = snap.left;
         } else if (dist.top < threshold) {
            attrs.scaleY = (h - (snap.top - target.top)) / target.height;
            attrs.scaleX = (attrs.scaleY / target.scaleY) * target.scaleX;
            attrs.left = attrs.left + (w - target.width * attrs.scaleX);
            attrs.top = snap.top;
         }
         break;
      case 'mt':
         if (dist.top < threshold) {
            attrs.scaleY = (h - (snap.top - target.top)) / target.height;
            attrs.top = snap.top;
         }
         break;
      case 'tr':
         if (dist.right < dist.top && dist.right < threshold) {
            attrs.scaleX = (snap.right - target.left) / target.width;
            attrs.scaleY = (attrs.scaleX / target.scaleX) * target.scaleY;
            attrs.top = target.top + (h - target.height * attrs.scaleY);
         } else if (dist.top < threshold) {
            attrs.scaleY = (h - (snap.top - target.top)) / target.height;
            attrs.scaleX = (attrs.scaleY / target.scaleY) * target.scaleX;
            attrs.top = snap.top;
         }
         break;
      case 'ml':
         if (dist.left < threshold) {
            attrs.scaleX = (w - (snap.left - target.left)) / target.width;
            attrs.left = snap.left;
         }
         break;
      case 'mr':
         if (dist.right < threshold) attrs.scaleX = (snap.right - target.left) / target.width;
         break;
      case 'bl':
         if (dist.left < dist.bottom && dist.left < threshold) {
            attrs.scaleX = (w - (snap.left - target.left)) / target.width;
            attrs.scaleY = (attrs.scaleX / target.scaleX) * target.scaleY;
            attrs.left = snap.left;
         } else if (dist.bottom < threshold) {
            attrs.scaleY = (snap.bottom - target.top) / target.height;
            attrs.scaleX = (attrs.scaleY / target.scaleY) * target.scaleX;
            attrs.left = attrs.left + (w - target.width * attrs.scaleX);
         }
         break;
      case 'mb':
         if (dist.bottom < threshold) attrs.scaleY = (snap.bottom - target.top) / target.height;
         break;
      case 'br':
         if (dist.right < dist.bottom && dist.right < threshold) {
            attrs.scaleX = (snap.right - target.left) / target.width;
            attrs.scaleY = (attrs.scaleX / target.scaleX) * target.scaleY;
         } else if (dist.bottom < threshold) {
            attrs.scaleY = (snap.bottom - target.top) / target.height;
            attrs.scaleX = (attrs.scaleY / target.scaleY) * target.scaleX;
         }
         break;
   }
   target.set(attrs);
});
&#13;
canvas {border: 1px solid #ccc}
&#13;
<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" width="600" height="600"></canvas>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

这是对已接受答案的更新。

target.getWidth()target.getHeight()似乎不再起作用了,所以我从接受的答案中更新了小提琴,用target.width * target.scaleXtarget.height * target.scaleY替换它们。

以下是updated fiddle

答案 2 :(得分:1)

以上答案似乎不再起作用。 调整对象大小时,结构似乎会更新scaleX / scaleY而不是宽度/高度。

这是一个新的小提琴:http://jsfiddle.net/ej2hrqm8/

我禁用了TL / TR / BL,因为我无法使这三个角落正常工作。

// Build using FabricJS v3.4

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

// create grid

for (var i = 0; i < (600 / gridSize); i++) {
  canvas.add(new fabric.Line([ i * gridSize, 0, i * gridSize, 600], { stroke: '#ccc', selectable: false }));
  canvas.add(new fabric.Line([ 0, i * gridSize, 600, i * gridSize], { stroke: '#ccc', selectable: false }))
}

// add objects

canvas.add(new fabric.Rect({ 
  left: 100, 
  top: 100, 
  width: 50, 
  height: 50, 
  fill: '#faa', 
  originX: 'left', 
  originY: 'top',
  centeredRotation: true
}));

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

function Snap(value)
{
  return Math.round(value / snapSize) * snapSize;
}

function SnapMoving(options)
{
  options.target.set({
    left: Snap(options.target.left),
    top: Snap(options.target.top)
  });
}

function SnapScaling(options)
{
  var target = options.target;
  var pointer = options.pointer;

  var px = Snap(pointer.x);
  var py = Snap(pointer.y);
  var rx = (px - target.left) / target.width;
  var by = (py - target.top) / target.height;
  var lx = (target.left - px + (target.width * target.scaleX)) / (target.width);
  var ty = (target.top - py + (target.height * target.scaleY)) / (target.height);

  var a = {};

  // Cannot get snap to work on some corners :-(
  switch (target.__corner)
  {
    case "tl":
      // Not working
      //a = { scaleX: lx, scaleY: ty, left: px, top: py };
      break;
    case "mt":
      a = { scaleY: ty, top: py };
      break;
    case "tr":
      // Not working
      //a = { scaleX: rx, scaleY: ty, top: py  };
      break;
    case "ml":
      a = { scaleX: lx, left: px };
      break;
    case "mr":
      a = { scaleX: rx };
      break;
    case "bl":
      // Not working
      //a = { scaleX: lx, scaleY: by, left: px };
      break;
    case "mb":
      a = { scaleY: by };
      break;
    case "br":
      a = { scaleX: rx, scaleY: by };
      break;
  }

  options.target.set(a);
}

canvas.on({
  "object:moving": SnapMoving,
  "object:scaling": SnapScaling,
});

答案 3 :(得分:1)

首先,以上所有答案均不适用于旋转对象,这对我来说意义重大。我花了几天时间进行网格捕捉,最后我找到了一个很好的解决方案,至少可以满足我的需求。第一次,我用学校数学:Math.tan +毕达哥拉斯定理进行了计算,但是这太难了,而且我很确定自己做错了。

首先找到并检查wrapWithFixedAnchor函数的工作方式。基本上,您可以更改目标的任何属性:width,height,scaleX,scaleY,然后wrapWithFixedAnchor将帮助您将目标转换到锚点。

这是工作示例:0

我不认为将其称为“捕捉”是个好主意,因为实际上只有当对象旋转零时,它才能捕捉到网格。目前,它的工作原理与http://jsfiddle.net/kod57cwb捕捉

非常相似