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 = 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...