Snapping object to grid

by Hooman Askari

HTML

<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" width="600" height="600"></canvas>

CSS

canvas {
    border: 1px solid #ccc;
}

JavaScript

// Build using FabricJS v3.4

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

// 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(event) {
  const {
    transform
  } = event;
  const {
    target
  } = transform;

  const targetWidth = target.width * target.scaleX;
  const targetHeight = target.height * target.scaleY;

  const snap = {
    // closest width to snap to
    width: Snap(targetWidth),
    height: Snap(targetHeight),
  };

  const threshold = gridSize;

  const dist = {
    // distance from current width to snappable width
    width: Math.abs(targetWidth - snap.width),
    height: Math.abs(targetHeight - snap.height),
  };

  const centerPoint = target.getCenterPoint();

  const anchorY = transform.originY;
  const anchorX = transform.originX;

  const anchorPoint = target.translateToOriginPoint(
    centerPoint,
    anchorX,
    anchorY,
  );

  const attrs = {
    scaleX: target.scaleX,
    scaleY: target.scaleY,
  };

  // eslint-disable-next-line default-case
  switch (transform.corner) {
    case 'tl':
    case 'br':
    case 'tr':
    case 'bl':
      if (dist.width < threshold) {
       ...