Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Extending Fabric.js with Custom Graphical Objects

Tech May 18 3

Fabric.js provides several built-in shapes such as Rect, Circle, and Triangle. Creating custom shapes is often necessary for specific project requirements. This involves defining custom subclasses, a process that benefits from a solid understanding of the HTML Canvas API.

The Concept of Subclasses in Fabric.js

Subclasses in Fabric.js follow similar principles to JavaScript ES6 classes. You can define a class and extend it to inherit properties and methods. For example, the official docuemntation demonstrates extending a Rect to create a LabeledRect with an additional text label.

Defining a Class with fabric.util.createClass

Use fabric.util.createClass to define a new class. The method accepts a configuration object. The initialize function serves as the constructor, handling the initialization of instance properties.

const Vector2D = fabric.util.createClass({
    initialize: function(xCoord, yCoord) {
        this.x = xCoord || 0;
        this.y = yCoord || 0;
    },
    getCoordinates: function() {
        return `(${this.x}, ${this.y})`;
    }
});

const myVector = new Vector2D(15, 20);
console.log(myVector.x); // 15
console.log(myVector.getCoordinates()); // "(15, 20)"

Creating a Subclass with Inheritance

To create a subclass, pass two arguments to fabric.util.createClass: the parent class and the subclass configuration object. Use this.callSuper to invoke the parent class's methods.

const ColoredVector = fabric.util.createClass(
    // Parent class
    Vector2D,
    // Subclass definition
    {
        initialize: function(xCoord, yCoord, vecColor) {
            this.callSuper('initialize', xCoord, yCoord);
            this.color = vecColor || '#000000';
        },
        getDescription: function() {
            const coords = this.callSuper('getCoordinates');
            return `Vector at ${coords} with color ${this.color}`;
        }
    }
);

const blueVector = new ColoredVector(5, 10, '#0000FF');
console.log(blueVector.getDescription()); // "Vector at (5, 10) with color #0000FF"

Extending Built-in Fabric.js Objects

Custom graphical elements should typically extend fabric.Object or one of its built-in shape descendants (like fabric.Rect). This grants the new object the standard Fabric.js capabilities such as controls, borders, and event handling.

const LabeledRectangle = fabric.util.createClass(
    fabric.Rect,
    {
        type: 'labeledRectangle',
        initialize: function(config) {
            config = config || {};
            this.callSuper('initialize', config);
            // Set default properties
            this.set({
                width: 120,
                height: 60,
                labelText: config.labelText || ''
            });
        },
        // Override the `toObject` method for serialization
        toObject: function() {
            return fabric.util.object.extend(
                this.callSuper('toObject'),
                { labelText: this.get('labelText') }
            );
        },
        // Custom rendering logic
        _render: function(ctx) {
            // Render the standard rectangle
            this.callSuper('_render', ctx);
            // Draw the custom label
            ctx.save();
            ctx.font = '16px Arial';
            ctx.fillStyle = this.labelColor || '#000';
            ctx.textAlign = 'center';
            ctx.fillText(
                this.labelText,
                0, // Center horizontally relative to object origin
                -this.height / 2 + 20 // Position above the rectangle
            );
            ctx.restore();
        }
    }
);

const canvas = new fabric.Canvas('canvasElement');
const myLabeledRect = new LabeledRectangle({
    left: 50,
    top: 50,
    fill: '#90EE90',
    labelText: 'Hello',
    labelColor: '#333'
});
canvas.add(myLabeledRect);

Because LabeledRectangle extends fabric.Rect, all rectangle properties remain available.

myLabeledRect.set({ rx: 15, ry: 15, strokeWidth: 3 });
canvas.renderAll();

Implementing a Custom Shape from Scratch

To create a completely new shape, extend the fabric.Object base class and implement the _render method using the native Canvas 2D API (ctx).

const ArcShape = fabric.util.createClass(fabric.Object, {
    type: 'arcShape',
    initialize: function(options) {
        this.callSuper('initialize', options);
        // Define default dimensions
        this.set({ width: 120, height: 80 });
    },
    _render: function(ctx) {
        const radius = this.width / 2;
        const centerY = 0; // Fabric origin is at the object's center

        ctx.beginPath();
        // Draw a 180-degree arc (semicircle)
        ctx.arc(0, centerY, radius, 0, Math.PI);
        ctx.closePath();

        // Apply styles from instance properties
        if (this.fill) {
            ctx.fillStyle = this.fill;
            ctx.fill();
        }
        if (this.stroke && this.strokeWidth) {
            ctx.lineWidth = this.strokeWidth;
            ctx.strokeStyle = this.stroke;
            ctx.stroke();
        }
    }
});

const myCanvas = new fabric.Canvas('myCanvas');
const myArc = new ArcShape({
    left: 80,
    top: 60,
    angle: 30,
    fill: '#FFB6C1',
    stroke: '#4682B4',
    strokeWidth: 4
});
myCanvas.add(myArc);

The _render method receives the Canvas rendering context (ctx). Mastery of the Canvas API is essential for drawing complex custom shapes.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.