I have a function that wraps textboxes in FabricJS so that they don't become too wide, (it is automatically line breaking when the the maximum width is reached). However it is causing the text cursor to get pushed behind by 1 character every time it adds a new line.
Look at this gif to fully see the problem in play a gif
I am using the following function to automatically line break the text box. To give some context, it checks if the length of a textLine exceeds the maxWidth, and if it is the case with the last word included but doesn't exceed if the last word is not included, then it adds a new line by entering \n
and somewhere here it causes the problem.
function wrapCanvasText(t, canvas, maxW, maxH) {
let initialFormatted = t.text
if (typeof maxH === "undefined") {
maxH = 0;
}
var words = t.text.split(" ")
var formatted = '';
// clear newlines
var sansBreaks = t.text.replace(/(\r\n|\n|\r)/gm, "");
// calc line height
var lineHeight = new fabric.Text(sansBreaks, {
fontFamily: t.fontFamily,
fontSize: t.fontSize
}).height;
// adjust for vertical offset
var maxHAdjusted = maxH > 0 ? maxH - lineHeight : 0;
var context = canvas.getContext("2d");
context.font = t.fontSize + "px " + t.fontFamily;
var currentLine = "";
var breakLineCount = 0;
for (var n = 0; n < words.length; n++) {
console.log(words[n])
var isNewLine = currentLine == " ";
var testOverlap = currentLine + ' ' + words[n] + ' ';
// are we over width?
var w = context.measureText(testOverlap).width;
if (w < maxW) { // if not, keep adding words
currentLine += words[n] + ' ';
formatted += words[n] += ' ';
} else {
// if this hits, we got a word that need to be hypenated
if (isNewLine) {
var wordOverlap = "";
// test word length until its over maxW
for (var i = 0; i < words[n].length; ++i) {
wordOverlap += words[n].charAt(i);
var withHypeh = wordOverlap + "-";
if (context.measureText(withHypeh).width >= maxW) {
// add hyphen when splitting a word
withHypeh = wordOverlap.substr(0, wordOverlap.length - 2) + "-";
// update current word with remainder
words[n] = words[n].substr(wordOverlap.length - 1, words[n].length);
formatted += withHypeh; // add hypenated word
break;
}
}
}
n--; // restart cycle
if (words[n+1] !== '') {
formatted += '\n';
breakLineCount++;
}
currentLine = "";
}
if (maxHAdjusted > 0 && (breakLineCount * lineHeight) > maxHAdjusted) {
// add ... at the end indicating text was cutoff
formatted = formatted.substr(0, formatted.length - 3) + "...\n";
break;
}
}
// get rid of empy newline at the end
formatted = formatted.substr(0, formatted.length - 1);
return formatted;
}
You can try out this snippet it is an approximate version of what I have, most importantly, it does have the same cursor problem. To try out the problem, edit the text directly in the canvas after initialization.
var canvas = new fabric.Canvas('c');
canvas.backgroundColor = "#F5F5F5";
var textArea = document.getElementById('addNote');
function checkForChange() {
var activeObject = canvas.getActiveObject();
let formatted1 = wrapCanvasText(activeObject, canvas, 400, 2000);
activeObject.text = formatted1;
}
function wrapCanvasText(t, canvas, maxW, maxH) {
let initialFormatted = t.text
if (typeof maxH === "undefined") {
maxH = 0;
}
var words = t.text.split(" ")
var formatted = '';
// clear newlines
var sansBreaks = t.text.replace(/(\r\n|\n|\r)/gm, "");
// calc line height
var lineHeight = new fabric.Textbox(sansBreaks, {
fontFamily: t.fontFamily,
fontSize: t.fontSize
}).height;
// adjust for vertical offset
var maxHAdjusted = maxH > 0 ? maxH - lineHeight : 0;
var context = canvas.getContext("2d");
context.font = t.fontSize + "px " + t.fontFamily;
var currentLine = "";
var breakLineCount = 0;
for (var n = 0; n < words.length; n++) {
console.log(words[n])
var isNewLine = currentLine == " ";
var testOverlap = currentLine + ' ' + words[n] + ' ';
// are we over width?
var w = context.measureText(testOverlap).width;
if (w < maxW) { // if not, keep adding words
currentLine += words[n] + ' ';
formatted += words[n] += ' ';
} else {
// if this hits, we got a word that need to be hypenated
if (isNewLine) {
var wordOverlap = "";
// test word length until its over maxW
for (var i = 0; i < words[n].length; ++i) {
wordOverlap += words[n].charAt(i);
var withHypeh = wordOverlap + "-";
if (context.measureText(withHypeh).width >= maxW) {
// add hyphen when splitting a word
withHypeh = wordOverlap.substr(0, wordOverlap.length - 2) + "-";
// update current word with remainder
words[n] = words[n].substr(wordOverlap.length - 1, words[n].length);
formatted += withHypeh; // add hypenated word
break;
}
}
}
n--; // restart cycle
if (words[n + 1] !== '') {
formatted += '\n';
breakLineCount++;
}
currentLine = "";
}
if (maxHAdjusted > 0 && (breakLineCount * lineHeight) > maxHAdjusted) {
// add ... at the end indicating text was cutoff
formatted = formatted.substr(0, formatted.length - 3) + "...\n";
break;
}
}
// get rid of empy newline at the end
formatted = formatted.substr(0, formatted.length - 1);
return formatted;
}
$("#addNote").keyup(function(e) {
var activeObject = canvas.getActiveObject();
if (activeObject && activeObject.type == 'textbox') {
activeObject.text = textArea.value
let formatted1 = wrapCanvasText(activeObject, canvas, 400, 2000);
activeObject.text = formatted1;
while (activeObject.textLines.length > 1 && canvas.getWidth() * 0.8 >= activeObject.width) {
activeObject.set({
width: activeObject.getScaledWidth() + 1
})
}
canvas.renderAll();
} else {
var textSample = new fabric.Textbox(textArea.value, {});
textSample.left = 0
textSample.splitByGrapheme = true
textSample.lockRotation = true
textSample.editable = true
textSample.perPixelTargetFind = false
textSample.hasControls = true
textSample.width = canvas.getWidth() * 0.8
textSample.height = canvas.getHeight() * 0.8
textSample.maxWidth = canvas.getWidth() * 0.8
textSample.maxHeight = canvas.getHeight() * 3
canvas.add(textSample);
canvas.setActiveObject(textSample);
canvas.renderAll();
}
canvas.on('text:changed', checkForChange)
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.5.0/fabric.min.js"></script>
<textarea id="addNote"></textarea>
<canvas id="c" width="400" height="400"></canvas>
from FabricJS automatically adding new lines to textboxes causes text cursor to move backwards
No comments:
Post a Comment