2

I have two canvas layer, stacked on each other. A top layer named top and a bottom layer named bottom. On the top layer I add some objects that are transparent and only show their handles. The bottom layer should clone the objects from the top, change some properties and draw them.

This works fines as long as I'm only moving a single object on the top layer. When I select a group of objects and start to move the whole group, something breaks. On the top layer everything works fine but the objects on the bottom layer are drawn into the upper left corner of the canvas. This is corrected only if I start to drag a single object around again.

I hope that someone can take a look and may have an idea what's going wrong :)


jsFiddle

var top = new fabric.Canvas("top", {renderOnAddRemove:false});
var bottom = new fabric.Canvas("bottom", {renderOnAddRemove:false});

var update = function () {  
  bottom.clear().add.apply(bottom, top.getObjects().map(function (o) {
    return o.clone().set({
      selectable: false,
      hasBorders: false,
      hasControls: false,
      fill: "rgba(255,0,0,.5)",
      globalCompositeOperation: "xor"
    });
  })).renderAll();
};

top.on({
  "object:modified": update
});
Fidel90
  • 1,828
  • 6
  • 27
  • 63

1 Answers1

2

Groups in fabric are tricky as all the transforms ('top', 'left', 'scaleX', 'scaleY') are relative to the parent group's positions which is all relative to the groups center. So when you want to extract the top level canvas positions you need to do some work to take the transforms from the group and apply it to the object. Internally in fabric this happens as you group and ungroup stuff.

In this example I make all objects have the same X and Y origin as a group's default origin which is center. This make the matrix calculations match up. If you want to keep the default top/left origin for shapes then additional math will need to be done to calculate the correct position.

var topCanvas = new fabric.Canvas("top", {renderOnAddRemove:false});
var bottomCanvas = new fabric.Canvas("bottom", {renderOnAddRemove:false});

fabric.Object.prototype.originX = 'center';
fabric.Object.prototype.originY = 'center';

var update = function () {  
  bottomCanvas.clear().add.apply(bottomCanvas, topCanvas.getObjects().map(function (o) {
    var clone = o.clone();
    clone.set({
      selectable: false,
      hasBorders: false,
      hasControls: false,
      fill: "rgba(255,0,0,.5)",
      globalCompositeOperation: "xor",
    });

    if (o.group) {
      var matrix = o.calcTransformMatrix();
      var transforms = fabric.util.qrDecompose(matrix);
      clone.set({
        angle: transforms.angle,
        skewX: transforms.skewX,
        skewY: transforms.skewY,
        scaleX: transforms.scaleX,
        scaleY: transforms.scaleY,
        top: transforms.translateY,
        left: transforms.translateX,
      });
    }
    return clone;
  })).renderAll();
};

topCanvas.on({
  "object:modified": update
});

topCanvas
  .add(new fabric.Rect({
    top: 50,
    left: 50,
    width: 100,
    height: 100,
    fill: "transparent"
  }))
  .add(new fabric.Rect({
    top: 100,
    left: 100,
    width: 100,
    height: 100,
    fill: "transparent"
  }))
  .add(new fabric.Rect({
    top: 150,
    left: 150,
    width: 100,
    height: 100,
    fill: "transparent"
  }))
  .renderAll();

update();
#cont {
  position: relative;
  width: 400px;
  height: 300px;
  border: 1px solid;
}

canvas, .canvas-container {
  position: absolute!important;
  left: 0!important;
  top: 0!important;
  width: 100%!important;
  height: 100%!important;
}
<div id="cont">
  <canvas id="bottom" width="400" height="300"></canvas>
  <canvas id="top" width="400" height="300"></canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.3/fabric.min.js"></script>
StefanHayden
  • 3,569
  • 1
  • 31
  • 38
  • This works great, thank you :) When moving a group left/top values seem to be 1px off. This gets corrected when moving a single object again. Do have an idea where this might come from? Anyway great answer! – Fidel90 Aug 25 '16 at 05:04
  • I see another problem: When scaling one of the rects before grouping, this rects position is off even more after moving the group :( Same happens with changed rotation. – Fidel90 Aug 25 '16 at 05:16
  • 1
    woops. this has something to do with how the group is transformed around the center. I'll take another crack at it soon. – StefanHayden Aug 25 '16 at 05:38
  • 1
    That'd be great :) I'm currently looking at http://stackoverflow.com/a/29926545/2577116 and http://stackoverflow.com/a/34530547/2577116 but these are not getting me any further... – Fidel90 Aug 25 '16 at 05:42
  • I updated the code to work. I had to make all objects have a center origin. I'm sure there is a way to keep the origin top/left but right now I'm not sure the best way to do that conversion. – StefanHayden Aug 29 '16 at 00:39