fitzmode
fitzmode

Reputation: 1066

Fabricjs: Editing Text in Group

Fabric.js does not support editing text objects when they are within a group. How can one achieve this feature?

Upvotes: 1

Views: 3193

Answers (1)

fitzmode
fitzmode

Reputation: 1066

The solution is based on ungrouping the group of objects while maintaining their state, adding the subTargetCheck:true option as true to fabric.Group objects that contain text you'd like to edit.

The ungrouping is achieved on a simulated double click event using a mousedown listener on the top level group of objects. A mousedown listener is also added to the text objects within the group.

Once the text is edited, the objects are regrouped by removing original objects and replacing them with a new group with the same properties on the 'editing:exited' event returned by fabric.js.

const canvas = new fabric.Canvas("canvas");
canvas.setWidth(350);
canvas.setHeight(350);

// Double-click event handler
const fabricDblClick = function(obj, handler) {
  return function() {
    if (obj.clicked) handler(obj);
    else {
      obj.clicked = true;
      setTimeout(function() {
        obj.clicked = false;
      }, 500);
    }
  };
};

// ungroup objects in group
let groupItems = [];
let ungroup = function(group) {
  groupItems = group._objects;
  group._restoreObjectsState();
  canvas.remove(group);
  for (var i = 0; i < groupItems.length; i++) {
    canvas.add(groupItems[i]);
  }
  canvas.renderAll();
};

// Re-group when text editing finishes
const firstText = new fabric.IText("First Text", {
  fontFamily: "Comic Sans",
  fontSize: 14,
  stroke: "#000",
  strokeWidth: 1,
  fill: "#000",
  left: 170,
  top: 60
});
const secondText = new fabric.IText("Second Text", {
  fontFamily: "Comic Sans",
  fontSize: 14,
  stroke: "#000",
  strokeWidth: 1,
  fill: "#000",
  left: 170,
  top: 80
});

firstText.on("editing:exited", () => {
  var items = [];
  groupItems.forEach(function(obj) {
    items.push(obj);
    canvas.remove(obj);
  });
  const newTextGroup = new fabric.Group(items.reverse(), {
    subTargetCheck: true
  });
  canvas.add(newTextGroup);
  newTextGroup.on(
    "mousedown",
    fabricDblClick(newTextGroup, obj => {
      ungroup(newTextGroup);
    })
  );
});

let addGroup = () => {
  const textGroup = new fabric.Group([secondText, firstText], {
    left: 50,
    top: 50,
    subTargetCheck: true
  });
  canvas.add(textGroup);
  textGroup.on(
    "mousedown",
    fabricDblClick(textGroup, obj => {
      if (isTextGroup(obj)) {
        obj.getObjects().forEach(item => {
          item.on("mousedown", e => {
            if (e.target.type === "i-text") {
              e.target.enterEditing();
              e.target.hiddenTextarea.focus();
            }
          });
        });
      }
      ungroup(textGroup);
    })
  );
};
addGroup();

let isTextGroup = object => {
  return object.getObjects().every(el => {
    return el.type === "i-text";
  });
};
.canvas {
    width: 350px;
    height: 350px;
    border: 1px solid Black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.6.0/fabric.min.js"></script>

<canvas id="canvas" class="canvas" ></canvas>

Upvotes: 1

Related Questions