Fabric.js - Text with free rectangular bounding box like Google slides

Viewed 1195

I am making drawing editor using Fabric.js. One of the requirements is to create flexible text box component (like Goole Slides or Power Point) whose bounding box can be adjusted without compromising text size and other text controls like scale, reposition... Currently, what fabric.js provides with fabric.Text is not sufficient for this task. So I am using fabric.Group but I am stack on selection issue. I cannot select sub-objects of group. How can I go about this issue. Any information is welcome

This is I want to achieve:

enter image description here

This is what I have now with raw fabric.Text object

enter image description here

This is what I have achieved with fabric.Group

enter image description here

Here is my class which is intended for Textbox

class Text extends fabric.Group {
  constructor(options) {
    super([], Object.assign(options));
    this.class = 'textbox';

    this.init();
  }

  init() {
    const left = this.left;
    const top = this.top;
    const width = this.width || 100;
    const height = this.height || 40;

    this.rect = new fabric.Rect({
      left,
      top,
      width,
      height,
      fill: 'rgba(0,0,0,0)',
      stroke: this.stroke,
      type: 'rect',
      originX: 'left',
      originY: 'top',
      strokeWidth: this.strokeWidth
    });

    this.text = new fabric.IText(this.text, {
      left: left + 20,
      top: top + 12,
      fontSize: this.fontSize,
      fontFamily: this.fontFamily,
      fontColor: this.fontColor,
      selectable: true
    });
    this.text.setCoords();

    this.text.setControlsVisibility({
      br: false, // middle top disable
      bl: false, // midle bottom
      tl: false, // middle left
      tr: false, // I think you get it,
      mb: false,
      ml: false,
      mt: false,
      mr: false,
      mtr: false
    });

    this.addWithUpdate(this.rect);
    this.addWithUpdate(this.text);
  }

  toObject() {
    const obj = super.toObject();
    return Object.assign(obj, {
      class: this.class
    });
  }
}

export default Text;
1 Answers

You can Use Fabric's TextBox If you want flexible width and height. But I think You also need border around text, For that Grouping is good Option but You have to adjust Width and Height of Rect based on Your Text. For that You need to override onKeyDown method of Textbox and add your own method for changing width and height of Rect. Of course you can also call that function on text:changed event. I did the same , I can't share full code but here's the hint

this.LimitedTextbox = fabric.util.createClass(fabric.IText, {
       //you can ignore it , here i setting fixed width to Itext
        initDimensions: function() {
            this.isEditing && this.initDelayedCursor();
            this.clearContextTop();
            if (this.__skipDimension) {
                return;
            }
            this._splitText();
            this._clearCache();
            if (this.path) {
            this.width = this.path.width;
            this.height = this.path.height;
            }
            else {
                let width = this.calcTextWidth();
                if(width>this.maxWidth){
                    this.width = width;
                }else{
                    this.width = this.maxWidth;
                }
                this.height = this.calcTextHeight();    
            }
            if (this.textAlign.indexOf('justify') !== -1) {
            this.enlargeSpaces();
            }
            this.saveState({ propertySet: '_dimensionAffectingProps' });
        },
        onKeyDown:function(e){
            //Can be Ignored , I wanted my textbox to have limited lines only
            if ((e.keyCode == 13 && this._textLines.length>=this.maxLines) || 
            (e.keyCode == 9 || e.keyCode == 27)) {
                this.exitEditing();
                self.lastSelected = undefined;
            }else{
                this.callSuper('onKeyDown',e);
                let maxLine = Math.max(...this.__lineWidths); 
                //This is What you need , A function to change border width.
                self.changeBorderWidth(this.toObject().groupId, this, { iWidth: this.width, iHeight: this.height,maxLine:maxLine,id:this.toObject().id },false,this.toObject().customType==='stamp_input');
                if(this.dynamicMinWidth>this.maxWidth){
                    this.width = this.dynamicMinWidth;
                }
            }
                
            
        },
    });

Edit: To add border to IText or Textbox https://stackoverflow.com/a/67055302/11036872

Related