2

I'm trying to copy & paste objects in my FabricJS project.

It's FabricJS version 2.3.3

With native FabricJS objects it works fine. Exactly like in the demo (http://fabricjs.com/copypaste).

But after I created my custom Class (like here for example http://fabricjs.com/cross) I'm unable to paste objects based on my custom Class. Copy is OK, just Paste function throws an error.

All I get is an error in Console log: this._render is not a function pointing to some line number within FabricJS library.

Can anybody tell why? Thanks!

Here is my custom Class:

const C_Cross = fabric.util.createClass(fabric.Object, {

    initialize: function(options) {
        this.callSuper('initialize', options);

        this.width = 100;
        this.height = 100;

        this.w1 = this.h2 = 100;
        this.h1 = this.w2 = 30;
    },

    _render: function (ctx) {
        ctx.fillRect(-this.w1 / 2, -this.h1 / 2, this.w1, this.h1);
        ctx.fillRect(-this.w2 / 2, -this.h2 / 2, this.w2, this.h2);
    }

});

This is HTML file: (BODY tag only)

<body>

<canvas id="c" width="1000" height="800" style="border:1px solid #ccc"></canvas>

<br>

<button onclick="testCopy()">COPY</button>
<button onclick="testPaste()">PASTE</button>


<script type="text/javascript">
    var TheCanvas = new fabric.Canvas('c');

    var myCross = new C_Cross({ top: 100, left: 100 });
    TheCanvas.add(myCross);
</script>

</body>

And here functions for Copy & Paste:

function testCopy(){
    TheCanvas.getActiveObject().clone(function(cloned) {
        TheClipboard = cloned;
    });
}

function testPaste(){
    TheClipboard.clone(function(clonedObj) {
        TheCanvas.discardActiveObject();
        clonedObj.set({
            left: clonedObj.left + 10,
            top: clonedObj.top + 10,
            evented: true,
        });
        if (clonedObj.type === 'activeSelection') {
            // active selection needs a reference to the canvas
            clonedObj.canvas = canvas;
            clonedObj.forEachObject(function(obj) {
                TheCanvas.add(obj);
            });
            // this should solve the unselectability
            clonedObj.setCoords();
        } else {
            TheCanvas.add(clonedObj);
        }
        TheClipboard.top += 10;
        TheClipboard.left += 10;
        TheCanvas.setActiveObject(clonedObj);
        TheCanvas.requestRenderAll();
    });
}

Here is the piece of code from FabricJS library where it crashes: On the last line of this function.

drawObject: function(ctx) {
      this._renderBackground(ctx);
      this._setStrokeStyles(ctx, this);
      this._setFillStyles(ctx, this);
      this._render(ctx); // <--- crashes here
},
Enriqe
  • 567
  • 1
  • 6
  • 22

1 Answers1

4

You need to add fromObject for your custom class. and need to define type same as class name which require to read while finding specific class.

DEMO

fabric.Cross = fabric.util.createClass(fabric.Object, {
  type: 'cross',
  initialize: function(options) {
    this.callSuper('initialize', options);

    this.width = 100;
    this.height = 100;

    this.w1 = this.h2 = 100;
    this.h1 = this.w2 = 30;
  },

  _render: function(ctx) {
    ctx.fillRect(-this.w1 / 2, -this.h1 / 2, this.w1, this.h1);
    ctx.fillRect(-this.w2 / 2, -this.h2 / 2, this.w2, this.h2);
  }

});

fabric.Cross.fromObject = function(object, callback) {
  var cross = new fabric.Cross(object);
  callback && callback(cross);
  return cross;
};

var TheCanvas = new fabric.Canvas('c');

var myCross = new fabric.Cross({
  top: 100,
  left: 100
});
TheCanvas.add(myCross);

function testCopy() {
  TheCanvas.getActiveObject().clone(function(cloned) {
    TheClipboard = cloned;
    console.log(TheClipboard)
  });
}

function testPaste() {
  TheClipboard.clone(function(clonedObj) {
    TheCanvas.discardActiveObject();
    clonedObj.set({
      left: clonedObj.left + 10,
      top: clonedObj.top + 10,
      evented: true,
    });
    if (clonedObj.type === 'activeSelection') {
      // active selection needs a reference to the canvas
      clonedObj.canvas = TheCanvas;
      clonedObj.forEachObject(function(obj) {
        TheCanvas.add(obj);
      });
      // this should solve the unselectability
      clonedObj.setCoords();
    } else {
      TheCanvas.add(clonedObj);
    }
    TheClipboard.top += 10;
    TheClipboard.left += 10;
    TheCanvas.setActiveObject(clonedObj);
    TheCanvas.requestRenderAll();
  });
}
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" width="400" height="400" style="border:1px solid #ccc"></canvas>
<br>
<button onclick="testCopy()">COPY</button>
<button onclick="testPaste()">PASTE</button>
Durga
  • 15,263
  • 2
  • 28
  • 52
  • Thank you! It works but now I'm stuck again - after I make some more copies and I select multiple of them - I can't Copy & Paste again. Copy function throws **fabric.util.getKlass(...).fromObject is not a function** – Enriqe Jun 25 '18 at 07:45
  • 1
    @Enriqe check updated, multiple object copy and paste also working – Durga Jun 25 '18 at 07:48
  • 1
    Thank you ! So the solution is: property **type** which must contain the **name of the Class** ! Super! – Enriqe Jun 25 '18 at 08:23