typescript FabricJS固定大小的TextBox,带有动态字体Ize(将文本缩小到适合大小)

kmpatx3s  于 2022-12-05  发布在  TypeScript
关注(0)|答案(2)|浏览(178)

如果文本框中的文本太大,如何修复文本框的大小并动态减小fontSize?是的,存在类似的问题here,但它只适用于一行文本。
我想实现的正是这个目标:(示例来自imgflip meme editor

我尝试了以下方法:

let text = new fabric.Textbox(box.text, {
    top: box.top,
    left: box.left,
    width: box.width,
});
if (text.width > box.width) {
    text.fontSize *= box.width / (text.width + 1);
    text.width = box.width;
}
if (text.height > box.height) {
    text.fontSize *= box.height / (text.height + 1);
    text.height = box.height;
}
canvas.add(text);

这样,fontSize会按照文本框的宽度或高度变化的比例减小。但这有时会导致文本变得非常小,因为文本不会得到很好的换行。fontSize和换行需要找到一个最佳值。有什么想法吗?谢谢!

5vf7fwbs

5vf7fwbs1#

我真的找到了一个解决办法。以防有人有同样的问题。
根据宽度调整字体大小可以很好地使用我的原始代码:

if (text.width > box.width) {
    text.fontSize *= box.width / (text.width + 1);
    text.width = box.width;
}

这只会调整非常长的单词的字体大小,因为文本框会自动换行。但是这种换行会导致高度在我的原始代码中收缩太多。为了考虑换行,我最终逐渐减小了字体大小,并通过每次调用canvas.renderAll()来重新计算文本换行:

while (text.height > box.height && text.fontSize > 12) {
    text.fontSize--;
    canvas.renderAll();
}

这可能是低效的,但它服务于我的用例。

blpfk2vs

blpfk2vs2#

文字换行和字体大小随便笺宽度和高度而改变。双击时激活编辑模式。

export const createStickyNotes = (canvas, options) => {
  fabric.StickyNote = fabric.util.createClass(fabric.Group, {
    type: "StickyNote",
    initialize: function (options) {
      this.set(options);
      var height = this.height;
      var width = this.width;

      this.rectObj = new fabric.Rect({
        width: width,
        height: height,
        fill: this.rectObj?.fill ?? "rgba(251,201,112,1)",
        originX: "center",
        originY: "center",
        objectCaching: false,
        stateProperties: ["fill"],
      });
      this.textObj = new fabric.Textbox(this.textObj?.text ?? "Notes", {
        originX: "center",
        originY: "center",
        textAlign: "center",
        width: 100,
        hasControls: false,
        fontSize: this.textObj?.fontSize ?? 30,
        lineHeight: 1,
        stateProperties: ["text", "fontSize"],
        scaleX: this.textObj?.scaleX ?? 1,
        scaleY: this.textObj?.scaleY ?? 1,
        objectCaching: false,
        breakWords: true,
        fontFamily: "Open Sans",
      });

      this._objects = [this.rectObj, this.textObj];
      //   this custom _set function will set custom properties value to object when it will load from json.
      // at that time loadFromJson function will call this initialize function.
      // this._setCustomProperties(this.options);
      canvas.renderAll();

      //evenet will fire if the object is double clicked by mouse
      this.on("mousedblclick", (e) => {
        var pasteFlag = false;
        var scaling = e.target.getScaledWidth() / 100;
        var textForEditing;
        canvas.bringToFront(e.target);
        e.target.selectable = false;
        const [rectObj, textObj] = this.getObjects();
        textObj.clone(function (clonedObj) {
          clonedObj.set({
            left: e.target.left,
            top: e.target.top,
            lockMovementY: true,
            lockMovementX: true,
            hasBorders: false,
            scaleX: scaling,
            scaleY: scaling,
            breakWords: true,
            width: textObj.width,
            stateProperties: [],
          });
          textForEditing = clonedObj;
        });

        this.remove(textObj);
        canvas.add(textForEditing);
        canvas.setActiveObject(textForEditing);

        textForEditing.enterEditing();
        textForEditing.selectAll();

        textForEditing.paste = (function (paste) {
          return function (e) {
            disableScrolling();
            pasteFlag = true;
          };
        })(textForEditing.paste);

        textForEditing.on("changed", function (e) {
          var fontSize = textForEditing.fontSize;
          var charCount = Math.max(textForEditing._text.length, 1);
          var charWR =
            (textForEditing.textLines.length * width) / (charCount * fontSize);

          if (textForEditing.height < height - 15) {
            fontSize = Math.min(
              Math.sqrt(
                ((height - 10 - fontSize) / 1.16) *
                  (width / (charCount * charWR))
              ),
              30
            );
          }
          if (textForEditing.height > height - 15) {
            fontSize = Math.sqrt(
              ((height - 10) / 1.16) * (width / (charCount * charWR))
            );
          }
          if (pasteFlag) {
            pasteFlag = false;
            while (
              textForEditing.height > height - 15 &&
              textForEditing.fontSize > 0
            ) {
              fontSize = textForEditing.fontSize -= 0.2;
              canvas.renderAll();
            }
          }
          textForEditing.fontSize = fontSize;
        });

        textForEditing.on("editing:exited", () => {
          enableScrolling();
          canvas.setActiveObject(textObj);
          textObj.set({
            text: textForEditing.text,
            fontSize: textForEditing.fontSize,
            visible: true,
          });
          this.add(textObj);
          this.selectable = true;
          canvas.remove(textForEditing);
          canvas.discardActiveObject();
        });
      });

      function disableScrolling() {
        var x = window.scrollX;
        var y = window.scrollY;
        window.onscroll = function () {
          window.scrollTo(x, y);
        };
      }

      var _wrapLine = function (_line, lineIndex, desiredWidth, reservedSpace) {
        var lineWidth = 0,
          splitByGrapheme = this.splitByGrapheme,
          graphemeLines = [],
          line = [],
          // spaces in different languges?
          words = splitByGrapheme
            ? fabric.util.string.graphemeSplit(_line)
            : _line.split(this._wordJoiners),
          word = "",
          offset = 0,
          infix = splitByGrapheme ? "" : " ",
          wordWidth = 0,
          infixWidth = 0,
          largestWordWidth = 0,
          lineJustStarted = true,
          additionalSpace = splitByGrapheme ? 0 : this._getWidthOfCharSpacing();

        reservedSpace = reservedSpace || 0;
        desiredWidth -= reservedSpace;
        for (var i = 0; i < words.length; i++) {
          // i would avoid resplitting the graphemes
          word = fabric.util.string.graphemeSplit(words[i]);
          wordWidth = this._measureWord(word, lineIndex, offset);
          offset += word.length;

          // Break the line if a word is wider than the set width
          if (this.breakWords && wordWidth >= desiredWidth) {
            if (!lineJustStarted) {
              graphemeLines.push(line);
              line = [];
              lineWidth = 0;
              lineJustStarted = true;
            }
            this.fontSize *= desiredWidth / (wordWidth + 1);
            // Loop through each character in word
            for (var w = 0; w < word.length; w++) {
              var letter = word[w];
              var letterWidth =
                (this.getMeasuringContext().measureText(letter).width *
                  this.fontSize) /
                this.CACHE_FONT_SIZE;
              line.push(letter);
              lineWidth += letterWidth;
            }
            word = [];
          } else {
            lineWidth += infixWidth + wordWidth - additionalSpace;
          }

          if (lineWidth >= desiredWidth && !lineJustStarted) {
            graphemeLines.push(line);
            line = [];
            lineWidth = wordWidth;
            lineJustStarted = true;
          } else {
            lineWidth += additionalSpace;
          }

          if (!lineJustStarted) {
            line.push(infix);
          }
          line = line.concat(word);

          infixWidth = this._measureWord([infix], lineIndex, offset);
          offset++;
          lineJustStarted = false;
          // keep track of largest word
          if (wordWidth > largestWordWidth && !this.breakWords) {
            largestWordWidth = wordWidth;
          }
        }

        i && graphemeLines.push(line);

        if (largestWordWidth + reservedSpace > this.dynamicMinWidth) {
          this.dynamicMinWidth =
            largestWordWidth - additionalSpace + reservedSpace;
        }

        return graphemeLines;
      };

      fabric.util.object.extend(fabric.Textbox.prototype, {
        _wrapLine: _wrapLine,
      });

      function enableScrolling() {
        window.onscroll = function () {};
      }
    },

    toObject: function (propertiesToInclude) {
      // This function is used for serialize this object. (used for create json)
      // not inlclude this.textObj and this.rectObj into json because when object will load from json, init fucntion of this class is called and it will assign this two object textObj and rectObj again.
      var obj = this.callSuper(
        "toObject",
        [
          "objectCaching",
          "textObj",
          "rectObj",
          // ... property list that you want to add into json when this object is convert into json using toJSON() function. (serialize)
        ].concat(propertiesToInclude)
      );
      // delete objects array from json because then object load from json, Init function will call. which will automatically re-assign object and assign _object array.
      delete obj.objects;
      return obj;
    },
  });

  fabric.StickyNote.async = true;
  fabric.StickyNote.fromObject = function (object, callback) {
    // This function is used for deserialize json and convert object json into button object again. (called when we call loadFromJson() fucntion on canvas)
    return fabric.Object._fromObject("StickyNote", object, callback);
  };

  return new fabric.StickyNote(options);
};
//How to use 

 var options = {
      width: 100,
      height: 100,
      originX: "center",
      originY: "center",
    };
    var notes = StickyNotes(canvas, options);
    canvas.add(notes);

相关问题