Currently resizing a text element simple scales the text. This shouldn't be the case, instead resizing should re-define the bounding box around the text.
You should also be able to change the behaviour of text overflow. This should be defined with a new (set|get)textOverflow property. Similar to the text-overflow CSS3 attribute. see: https://developer.mozilla.org/en/CSS/text-overflow
Similarly wrapping behaviour should be defined by the use of a new (set|get)wordWrap property. Similar to the CSS3 word-wrap attribute. See: https://developer.mozilla.org/en/CSS/word-wrap
Having it this way also then makes more sense for the textAlign properties. Text alignment should then be relative to the bounding box. Not the whole canvas! I'm not sure how you use these at the moment, but it's confusing none the less.
I know re-writing the text element to remove cufon dependency is high on the Todo list, so when you start doing that if you could include the above changes I'd really appreciate it. Also looking forward to removing cufon, being able to use any css font will be so much better.
I'd be happy to help contribute but I have no idea how far through the rewrite you already are. Please advise when/where you'd need help doing this.
Thanks
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
We could change text model to act like this, but I'm not sure it's better usability-wise.
Why would user would want to resize text object boundaries without resizing text? To increase padding? To let the text flow differently? Anything else?
All objects in fabric currently behave the same way, and text ones are no exception. We can make them exception, of course, but it seems like current behavior is actually pretty convenient. You can resize text by resizing boundaries; no need to worry about resizing text, and also resizing boundaries.
As far as text alignment โ it already works relative to text objects. You can only see it in multiline text, though, when text lines are not of the same width.
Any thoughts on this?
@kangax sorry didn't have time to reply to this! I think it's 100% necessary!
Fabric.js is a great tool for creating a wysiwyg pdf print document or alike. That's what I'm using it for anyway. Part of this means that the user can enter text, but should be allowed to add dynamic fields like {first_name} or {last_name} etc. If the text was all static then it would be fine. But because it's dynamic then it no longer works, and instead you should be able to define the bounding box.
My application is made on Rails and i'm using Prawn to convert documents to PDF. The text_box method in prawn has a very different (and in my option - correct) way of dealing with the concept of a "bounding box". See http://prawn.majesticseacreature.com/docs/0.11.1/Prawn/Text.html#method-i-text_box for details
I think Ollym got the point.
This is it by default when resizing div or other html elements I think we don't include resizing the font but instead
we either hide the rest or show a scroll bar. depends on the overflow value.
Because the current set-up defeated the purpose of fontSize attribute.
Just sharing my thoughts, especially on my application the scenario is that shapes are editable with text.
To do this I have to group a text canvas object into another object (triangle, rectangle etc.) but my current problem is that there is no bounding area when the text reaches more than the width of the object. It means the canvas text object does not do default wrapping the same way html elements does.
Unless there's another way to put a text into the shapes and that would be great.
thanks!
@kangax thoughts? Can you please have another look at this? Thanks
@ollym I reopened the ticket and marked it as possible feature. I'm open to having this kind of (alternative) behavior in Fabric. Probably controlled by a boolean property on text instances. Don't think this will be a priority at the moment, but I'll have a look when I get a chance. Or perhaps someone can tinker and contribute.
Ya, we definitely need this.
@kangax i have a issue ,can we place the text where we want like for example in the canvas i want it at (183,178) ? can we achieve this by any way?
:+1: for this feature. Would be very handy indeed!
:+1:
Kangax, do you have any idea how this feature can be implemented?
Here's a tiny snippet of what we've come up with for today. Might be of some use to others for the short term or give @kangax some inspiration for the moment :)
<script>
function _wrapText (fabric_canvas, text, x, y, maxWidth, lineHeight) {
var lines = text.split("\n");
var wrapped_text = [];
for (var l = 0; l < lines.length; l++) {
var line = "";
var words = lines[l].split(" ");
for (var w = 0; w < words.length; w++) {
var testLine = line + words[w] + " ";
var metrics = fabric_canvas.getContext('2d').measureText(testLine);
console.log('metric', metrics);
var testWidth = metrics.width;
if (testWidth > maxWidth) {
// add new fabric.Text
//text_options.top = text_options.top + y;
wrapped_text.push(line);
//ctx.fillText(line, x, y);
line = words[w] + " ";
//y += lineHeight;
} else {
line = testLine;
}
}
//text_options.top = text_options.top + y;
wrapped_text.push(line);
//ctx.fillText(line, x, y);
//y += lineHeight;
}
return wrapped_text.join("\n");
}
function moveText(text, parent)
{
text.set('top', parent.get('top'));
text.set('left', parent.get('left'));
text.text = _wrapText(
canvas,
text.get('original_text'),
parent.get('left'),
parent.get('top'),
(parent.get('width') * parent.get('scaleX')) / 2,
20
);
console.log(parent);
console.log(text);
}
var canvas = new fabric.Canvas('fabric-canvas');
var text = new fabric.Text(
"Do stuff\n with me! and stuff and stuff and stuff and stuff. And more stuff.and stuff and stuff. And more stuff.and stuff and stuff. And more stuff.and stuff and stuff. And more stuff.and stuff and stuff. And more stuff.",
{
original_text: "Do stuff\n with me! and stuff and stuff and stuff and stuff. And more stuff.and stuff and stuff. And more stuff.and stuff and stuff. And more stuff.and stuff and stuff. And more stuff.and stuff and stuff. And more stuff.",
fontSize: 20,
top: 100,
left: 100,
lockScalingX: true,
lockScalingY: true,
selectable: false
}
);
var rect = new fabric.Rect({
text_field: text,
width: 200,
height: 50,
fill: '#faa',
rx: 10,
ry: 10,
top: 100,
left: 100
});
canvas.add(rect);
canvas.add(text);
canvas.on('object:scaling', function (event) {
active_object = canvas.getActiveObject();
if (active_object.text_field && active_object.text_field.get('original_text')) {
moveText(text, active_object);
}
});
canvas.on('object:moving', function (event) {
active_object = canvas.getActiveObject();
if (active_object.text_field && active_object.text_field.get('original_text')) {
moveText(text, active_object);
}
});
</script>
May I suggest you leave fabric.Text just as it is, so to be backwards compatible. And create a new class.. fabric.TextBox which extends fabric.Text but also supports: wordWrap, textOverflow.
I needed this functionality as well, and wrote a little function that takes fabric.Text and returns a formatted copy.
http://www.enkolab.com/code/wrapping-text-for-fabric-js/
///
/// t:fabric.IText, maxW:number, maxH:number, canvas:HTMLCanvas
///
function wrapCanvasText(t, canvas, maxW, maxH ) {
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++) {
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 words 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) + "-";
// reset current word
words[n] = words[n].substr(wordOverlap.length - 1, words[n].length);
formatted += withHypeh; // add hypenated word
break;
}
}
}
n--; // restart cycle
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);
var ret = new fabric.Text(formatted, { // return new text-wrapped text obj
left: t.left,
top: t.top,
fill: t.fill,
fontFamily: t.fontFamily,
fontSize: t.fontSize
});
return ret;
};
We needed this feature in our project also. So we used the example provided by @darrennolan to create a Textbox class.
It is a fabric object that creates Text objects for the text and allows the use (or not) of background images. When you change the object dimensions, the background scales, but the text not. Additionally, it allows horizontal and vertical alignment.
We use this to create dialog baloons. So we implemented a textPadding property, that is used to apply padding to the text based in the box image. This padding can be scaled with the box or not (ex.: rounded dialog boxes).
The class hasn't qunit tests yet. This is the reason why I didn't tried to push to github. But it is working very well in our tests. As soon as we create qunit tests to it, we push to @kangax analyse.
The class:
(function(global) {
"use strict";
/**
* fabric.Textbox A class to create TextBoxes, with or without images as their boxes
*/
var fabric = global.fabric || (global.fabric = { }), extend = fabric.util.object.extend, clone = fabric.util.object.clone, toFixed = fabric.util.toFixed;
if (fabric.Textbox) {
fabric.warn('fabric.Textbox is already defined');
return;
}
if (!fabric.Object) {
fabric.warn('fabric.Textbox requires fabric.Object');
return;
}
var stateProperties = fabric.Object.prototype.stateProperties.concat();
// properties for the box and the text
var newProperties = [
'fontFamily',
'fontWeight',
'fontSize',
'path',
'text',
'textDecoration',
'textShadow',
'textAlign',
'fontStyle',
'lineHeight',
'backgroundColor',
'textBackgroundColor',
'useNative',
'originalText',
'textPadding',
'boxPath',
'vAlign',
'boxImageScaleX',
'boxImageScaleY'
];
stateProperties = stateProperties.concat(newProperties);
/**
* Textbox class
* @class fabric.Textbox
* @classdesc Permits the creation of text boxes
* @extends fabric.Object
* @borrows fabric.Text as textObject
* @borrows fabric.Pathgroup as boxImage
* @return {fabric.Textbox} thisArg
*/
fabric.Textbox = fabric.util.createClass(fabric.Object, /** @lends fabric.Textbox.prototype */
{
/**
* Type of an object
* @type String
* @default
*/
type: 'textbox',
/**
* Font size (in pixels)
* @type Number
* @default
*/
fontSize: 40,
/**
* Font weight (e.g. bold, normal, 400, 600, 800)
* @type Number
* @default
*/
fontWeight: 'normal',
/**
* Font family
* @type String
* @default
*/
fontFamily: 'Times New Roman',
/**
* Text decoration Possible values: "", "underline", "overline" or "line-through".
* @type String
* @default
*/
textDecoration: '',
/**
* Text shadow
* @type String | null
* @default
*/
textShadow: '',
/**
* Text alignment. Possible values: "left", "center", or "right".
* @type String
* @default
*/
textAlign: 'left',
/**
* Font style . Possible values: "", "normal", "italic" or "oblique".
* @type String
* @default
*/
fontStyle: '',
/**
* Line height
* @type Number
* @default
*/
lineHeight: 1.3,
/**
* Background color of an entire text box
* @type String
* @default
*/
backgroundColor: '',
/**
* Background color of text lines
* @type String
* @default
*/
textBackgroundColor: '',
/**
* URL of a font file, when using Cufon
* @type String | null
* @default
*/
path: null,
/**
* Indicates whether canvas native text methods should be used to render text (otherwise, Cufon is used)
* @type Boolean
* @default
*/
useNative: true,
/**
* List of properties to consider when checking if state of an object is changed ({@link fabric.Object#hasStateChanged})
* as well as for history (undo/redo) purposes
* @type Array
*/
stateProperties: stateProperties,
/**
* The text that was setted when the object was instantiated
* @type String
* @default
*/
originalText: '',
/**
* The padding of the text, relative to the "box"
* @type Integer
* @default
*/
textPadding: 10,
/**
* Defines if the textPadding must be scaled with the box
* @type Boolean
* @default
*/
scaleTextPadding: true,
/**
* The fabric.Pathgroup object that holds the box image
* @type fabric.Pathgroup
* @default
*/
boxImage: null,
/**
* The path of the image of the box
* @type String
* @default
*/
boxPath: '',
/**
* Vertical alignment of the text relative to the box. Possible values: top, center, bottom
* @type String
* @default
*/
vAlign: 'center',
/**
* Original scales of the box. Used when rendering the text
* @private
* @type Array
*/
originalScales: null,
/**
* The scaleX property that needed to be applied to the box
* @type Integer
* @default
*/
boxImageScaleX: 1,
/**
* The scaleY property that needed to be applied to the box
* @type Integer
* @default
*/
boxImageScaleY: 1,
/**
* The fabric.Text object that holds the text
* @type fabric.Text
*/
textObject: null,
/**
* Color of object's fill
* @type String
* @default
*/
fill: "rgba(221,204,197,0.6)",
/**
* @method initialize
* @param {String} text
* @param {Object} options
* @param {Function} cb callback to be called when the box image is loaded
*/
initialize: function(text, options, cb)
{
this.callSuper('initialize', options);
this.setOptions(options);
this.setCoords();
this.originalText = text;
var _this = this;
var createBox = function(objects, options) {
var boxImage = {};
if (objects.length > 1) {
var opts = clone(options);
boxImage = new fabric.PathGroup(objects, opts);
} else {
if (Object.prototype.toString.call(objects) === "[object Array]") {
boxImage = objects[0];
} else {
boxImage = objects;
}
}
var sx = (_this.get('width') / boxImage.get('width')),
sy = ((_this.get('height') + 20) / boxImage.get('height'));
_this.boxImageScaleX = sx;
_this.boxImageScaleY = sy;
boxImage.set({
angle : 0,
scaleX : sx,
scaleY : sy,
hasRotatingPoint : false,
lockScalingX: true,
lockScalingY: true,
selectable: false,
useNative : true
});
boxImage.setSourcePath(_this.boxPath);
boxImage.set('_id', _this._id);
_this.boxImage = boxImage;
createText(text, options);
};
var createText = function(text, options) {
_this.textObject = new fabric.Text(text, options);
_this.textObject.originalText = text;
_this._applyPropertiesToText(options);
//_this.boxImage && _this.boxImage.sendBackwards();
if (cb) {
cb();
}
};
// loads the box image, if set
if (this.boxPath) {
if (this.boxPath.match(/svg$/)) {
fabric.loadSVGFromURL(this.boxPath, createBox);
} else {
fabric.Image.fromURL(this.boxPath, createBox);
}
} else {
createText(text, options);
}
},
/**
* Returns object representation of an instance
* @method toObject
* @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
* @return {Object} Object representation of an instance
*/
toObject: function(propertiesToInclude) {
if (!propertiesToInclude){
propertiesToInclude = [];
}
propertiesToInclude = propertiesToInclude.concat(newProperties);
return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude));
},
/**
* Toggles specified property from `true` to `false` or from `false` to `true` in the Textbox object and the refered fabric.Pathgroup child object
* @method toggle
* @param {String} property property to toggle
* @return {fabric.Object} thisArg
* @chainable
*/
toggle: function(property) {
this.boxImage.toggle(prop);
return this.callSuper('toggle', prop);
},
/**
* Calculate and return the textScale, based on the scaling of the object
* @method getTextPadding
* @param {String} scale The type of the scale. Possible values are "y" and "x"
* @return Number
*/
getTextPadding: function(scale) {
if (!scale) scale = "x";
else scale = scale.toLowerCase();
if (!this.boxImage || !this.scaleTextPadding) {
return this.textPadding;
} else {
var scales = {}, originalScales = this.originalScales;
if (originalScales) {
scales.x = originalScales[0];
scales.y = originalScales[1];
} else {
scales.x = this.get("scaleX");
scales.y = this.get("scaleY");
}
return this.textPadding * scales[scale];
}
},
/**
* Copied from fabric.Object.render, but with little modifications
* @method render
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {Boolean} noTransform
*/
render: function(ctx, noTransform) {
// do not render if width or height are zeros
if (this.width === 0 || this.height === 0) return;
ctx.save();
this._preRenderTransform(ctx, noTransform, false);
this._render(ctx, noTransform);
if (this.active && !noTransform) {
this.drawBorders(ctx);
this.drawControls(ctx);
}
ctx.restore();
if (this.boxImage) {
// move and render the box image
this._moveImageBox(ctx);
this.boxImage.render(ctx, true);
}
if (this.textObject) {
ctx.save();
this._preRenderTransform(ctx, noTransform, true);
this._applyPropertiesToText();
// move and render text
this._moveText(ctx);
this.textObject.render(ctx, true);
if (!noTransform) {
this.scaleX = this.originalScales[0];
this.scaleY = this.originalScales[1];
this.originalScales = null;
}
ctx.restore();
}
},
/**
* Transforms context before to render an object
* @method _preRenderTransform
* @param {CanvasRenderingContext2D} ctx Context
* @param {Boolean} noTransform If transform shouldn't be applyed
* @param {Boolean} noScale True if scale(x|y) must be reseted to 1, false if not
*/
_preRenderTransform: function(ctx, noTransform, noScale) {
var m = this.transformMatrix;
if (m && !this.group) {
ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);
}
if (!noTransform) {
if (noScale) {
this.originalScales = [this.scaleX, this.scaleY];
this.scaleX = 1;
this.scaleY = 1;
}
this.transform(ctx);
}
if (m && this.group) {
ctx.translate(-this.group.width/2, -this.group.height/2);
ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
}
},
/**
* Apply a background in the text if no box selected
* @method _render
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_render: function(ctx)
{
// show a background in the text, if selected
if (this.isActive() && !this.boxImage) {
ctx.save();
var x = -this.width / 2,
y = -this.height / 2,
w = this.width,
h = this.height;
ctx.fillStyle = this.fill.toLive
? this.fill.toLive(ctx)
: this.fill;
ctx.fillRect(x, y, w, h);
ctx.restore();
}
},
/**
* Adjust the text position in the canvas
* @method _moveText
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_moveText: function(ctx) {
this.textObject.set('text', this._wrapText(
ctx,
this.textObject.get('originalText')
));
var scaleX = this.originalScales ? this.originalScales[0] : this.scaleX;
var scaleY = this.originalScales ? this.originalScales[1] : this.scaleY;
var x = 0, y = 0;
// horizontal alignment
if (this.textAlign == 'left') {
x = x - ((this.get("width") * scaleX) / 2) + (this.textObject.get('width') / 2) + (this.getTextPadding('x'));
} else if (this.textAlign == 'right') {
x = x + ((this.get("width") * scaleX) / 2) - (this.textObject.get('width') / 2) - (this.getTextPadding('x'));
}
// vertical alignment
if (this.vAlign == "top") {
y = y + ((this.textObject.get('height') / 2)
- ((this.get('height')*scaleY)/2)
) + (this.getTextPadding('y'));
} else if (this.vAlign == 'bottom') {
y = y - ((this.textObject.get('height') / 2)
- ((this.get('height')*scaleY)/2)
) - (this.getTextPadding('y'));
}
// left and top are related to current context transform (the transform of textbox object)
this.textObject.set("left", x);
this.textObject.set("top", y);
},
/**
* Break the text accordingly to the width of Textbox. Based on the code of Darren Nolan (@darrennolan)
* @method _wrapText
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {String} Text to wrap
* @return {Array} Array with the lines of the text
*/
_wrapText: function (ctx, text) {
var scaleX = (this.originalScales ? this.originalScales[0] : this.scaleX);
var maxWidth = (this.width * scaleX),
lines = text.split("\n"),
wrapped_text = [];
var maximum=0;
// pass the text properties to the canvas context
this._setTextStyles(ctx);
for (var l = 0; l < lines.length; l++) {
var line = "";
var words = lines[l].split(" ");
for (var w = 0; w < words.length; w++) {
var testLine = line + words[w] + " ";
var metrics = ctx.measureText(testLine);
var testWidth = metrics.width;
if (testWidth > (maxWidth - (this.getTextPadding('x') * 2))) {
wrapped_text.push(line);
line = words[w] + " ";
} else {
line = testLine;
maximum = Math.max(testWidth, maximum);
}
}
wrapped_text.push(line);
}
return wrapped_text.join("\n");
},
/**
* Set the text properties. Copied from fabric.Text
* @method _setTextStyles
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_setTextStyles: function(ctx) {
ctx.fillStyle = this.fill.toLive?
this.fill.toLive(ctx)
: this.fill;
ctx.strokeStyle = this.strokeStyle;
ctx.lineWidth = this.strokeWidth;
ctx.textBaseline = 'alphabetic';
ctx.textAlign = this.textAlign;
ctx.font = this._getFontDeclaration();
},
/**
* Get the font declaration accordingly to the canvas way. Copied from fabric.Text
* @method _getFontDeclaration
* @return {String} Font properties declarations
*/
_getFontDeclaration: function() {
return [
// node-canvas needs "weight style", while browsers need "style weight"
(fabric.isLikelyNode ? this.fontWeight : this.fontStyle),
(fabric.isLikelyNode ? this.fontStyle : this.fontWeight),
this.fontSize + 'px',
(fabric.isLikelyNode ? ('"' + this.fontFamily + '"') : this.fontFamily)
].join(' ');
},
/**
* Apply the text properties to the fabric.Text object
* @method _applyPropertiesToText
* @param {Array} Properties
* @return {Array} Options applied to text object
*/
_applyPropertiesToText: function(options) {
var text_opts = {};
if (options) {
text_opts = fabric.util.object.clone(options);
} else {
for (var i = 0; i < newProperties.length; i++) {
text_opts[newProperties[i]] = this.get(newProperties[i]);
}
}
// locks the text object
text_opts.lockScalingX = true;
text_opts.lockScalingY = true;
text_opts.selectable = false;
text_opts.scaleX = 1;
text_opts.scaleY = 1;
text_opts.text = this.originalText;
if (this.textObject)
this.textObject.setOptions(text_opts);
return text_opts;
},
/**
* Applyes the coordinates and dimensions to the box image
* @method _moveImageBox
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_moveImageBox: function(ctx) {
var x = (this.get('left') + (this.get('width') / 2)),
y = (this.get('top'));
this.boxImage.left = x - (this.get('width')/2);
this.boxImage.top = y;
this.boxImage.angle = this.get('angle');
var w = (this.get('width') * this.get('scaleX')),
h = (this.get('height') * this.get('scaleY'));
var sx = (w / this.boxImage.width),
sy = ((h) / this.boxImage.height);
this.boxImage.scaleX = sx;
this.boxImage.scaleY = sy;
this.boxImageScaleX = sx;
this.boxImageScaleY = sy;
}
});
/**
* @method fromObject
* @static
* @param {Object} object
* @return {fabric.Textbox}
*/
fabric.Textbox.fromObject = function(object) {
var instance = new fabric.Textbox(object.originalText, clone(object), function() {
return instance && instance.canvas && instance.canvas.renderAll();
});
return instance;
};
})( typeof exports != 'undefined' ? exports : this);
Examples:
// text box, 120x60, without background image
var txtBox = new fabric.Textbox("A Great text to be rendered in the screen, wraped", {
fontSize: 18,
fontFamily: 'Arial',
textAlign: 'center',
width: 120,
height: 60
});
// text box, 120x60, with background image, scaled to the textbox dimensions
var txtBox = new fabric.Textbox("A Great text to be rendered in the screen, wraped", {
fontSize: 18,
fontFamily: 'Arial',
textAlign: 'center',
width: 120,
height: 60,
boxPath; 'path/to/your/image.svg'
});
// text box, 120x60, without background image, aligned at right + bottom
var txtBox = new fabric.Textbox("A Great text to be rendered in the screen, wraped", {
fontSize: 18,
fontFamily: 'Arial',
textAlign: 'right',
vAlign: 'bottom',
width: 120,
height: 60
});
@charlesschaefer can you move that into a pull-request? It's not very friendly reviewing it in a comment post
@ollym I've posted here because there's no tests to the class. But I'll send a pull-request so.
@charlesschaefer I am using your textbox object but i have one issue when i re size the canvas i want it to behave like fabric text object (scaling of text ) and when i am re sizing the object then there should not be any scaling just like now in textbox object.
I want to enable the scaling in one case(when i am resizing the canvas) .so can you please tell me what should i do to behave like normal fabric text object (i mean to scaled the text) and textbox in other case
@charlesschaefer thanks for the effort. I don't know why the request was closed so quickly without reason, quite rude really given the time and effort you clearly put into building it. Keep it going on your fork, I need this feature so I'll use your code base from there.
+1 this feature will be gr8
+1 for this feature
needed it for my own meme generator which was coded fully on fabricjs
here some snippet http://www.enkolab.com/code/wrapping-text-for-fabric-js/ which i am currently using in my project for word-wrapping
maybe it helps someone
Cool... :) thanks @investguy
On Sat, Nov 2, 2013 at 3:10 AM, investguy [email protected] wrote:
here some snippet http://www.enkolab.com/code/wrapping-text-for-fabric-js/which i am currently using in my project for word-wrapping
maybe it helps someoneโ
Reply to this email directly or view it on GitHubhttps://github.com/kangax/fabric.js/issues/187#issuecomment-27592540
.
Eldridge L. Torrefranca
+1 this feature
I've started using Fabric.js again and have a painful need for this feature. Can't believe it hasn't been taken on officially. Guess I'll have to do it myself...
Can't believe it hasn't been taken on officially
Well... this is because I spent last few months working on interactive text โ http://fabricjs.com/test/misc/itext.html โ the kind that is capable of partial text formatting, that works out of the box on mobile, that supports mouse/touch -based selection of text that we're all used to in editors elsewhere, that works seamlessly with dozens of keyboard shortcuts, the kind that simply doesn't exist in any other canvas library out there, and with functionality on a level of gigantic enterprise solutions like google docs or icloud.com. So yeah, there's only so much time in a day :)
haha yea I'm using itext but my app looks something like this:
https://www.dropbox.com/s/9wpaxfp1qkpofxu/Screenshot%202013-11-29%2011.31.16.png
(app is called TicketingHub.com)
So being able to edit the text in-context as supposed to having a little button in the toolbar called "Edit Text" doesn't make much of a difference to me.
The problem comes when I'm saving the template elements then rendering them using PrawnPDF and using Liquid to mail-merge the variables in I have no idea what the value of "width" should be for the text box....
Also when I have a fontSize set, this is simply overridden when the user resizes the text element. So this means you're scaling a fixed font size which doesn't make sense.
I'd be interested to see what other people are using fabric.js for, my use case is quite specific but I can imagine others are very similar.
Similar use case as ollym. + for bounding box based text. the question about what happens when overflow occurs... do nothing, clip?
I'm trying to use this textBox class but I'm getting and error. Where is isActive() defined? This is referenced in line 440ish "if (this.isActive() && .."
+1
+1
@charlesschaefer how would I use the textbox class? I'm also getting the same errors as jrett
I also encountered the need to do this with one of my old side projects. The way I solved this was to insert the desired text into a <div> element, wrapping each word in a <span> tag. When it came time to actually rendering it onto the canvas, I would use the relative positions of each SPAN tag to determine where to call ctx.fillText(). This allowed me to use the browser to do my word-breaks. You will need to be a little more intelligent to handle CKJ.
After a lot of searching, I decide to combine the solution and adapt it for my current project. The code is incomplete and not pretty good but it worked for me ( Textbox extending an IText ).
(function() {
var clone = fabric.util.object.clone;
fabric.Textbox = fabric.util.createClass(fabric.IText, fabric.Observable, {
/**
* Type of an object
* @type String
* @default
*/
type: 'textbox',
/**
* Constructor
* @param {String} text Text string
* @param {Object} [options] Options object
* @return {fabric.Textbox} thisArg
*/
initialize: function(text, options) {
this.styles = options ? (options.styles || { }) : { };
options.originY = "top";
this.callSuper('initialize', text, options);
},
/**
* Break the text accordingly to the width of Textbox. Based on the code of Darren Nolan (@darrennolan)
* @method _wrapText
* @param {CanvasRenderingContext2D} ctx Context to render on
* @param {String} Text to wrap
* @return {Array} Array with the lines of the text
*/
_wrapText: function (ctx, text) {
var scaleX = (this.originalScales ? this.originalScales[0] : this.scaleX);
var maxWidth = (this.width * scaleX),
lines = text.split(this._reNewline),
wrapped_text = [];
var maximum=0;
for (var l = 0; l < lines.length; l++) {
var line = "";
var words = lines[l].split(" ");
for (var w = 0; w < words.length; w++) {
var testLine = line + words[w] + " ";
var metrics = ctx.measureText(testLine);
var testWidth = metrics.width;
if (testWidth > (maxWidth )) {
wrapped_text.push(line);
line = words[w] + " ";
} else {
line = testLine;
maximum = Math.max(testWidth, maximum);
}
}
wrapped_text.push(line.trim());
}
return wrapped_text;
},
/**
* @private
* @param {CanvasRenderingContext2D} ctx Context to render on
*/
_renderViaNative: function(ctx) {
this._setTextStyles(ctx);
var coef = this.originX=="left"? 1:(this.originX=="right"?-1:0);
ctx.translate(this.left+coef*this.width/2, this.top+this.height/(2*this.lineHeight));
var textLines = this._wrapText(ctx, this.text);
var txtH = this._getTextHeight(ctx, textLines);
var lHeight = this._getTextHeight(ctx, ["A"]);
if(txtH>this.height*this.scaleY){
var lcount = (txtH - this.height*this.scaleY) / (lHeight);
for(var ln=0;ln<lcount; ln++) textLines.pop();
}
this.clipTo && fabric.util.clipContext(this, ctx);
this._renderTextBackground(ctx, textLines);
this._translateForTextAlign(ctx);
this._renderText(ctx, textLines);
if (this.textAlign !== 'left' && this.textAlign !== 'justify') {
ctx.restore();
}
this._renderTextDecoration(ctx, textLines);
this.clipTo && ctx.restore();
this._setBoundaries(ctx, textLines);
this._totalLineHeight = 0;
},
/**
* Returns object representation of an instance
* @method toObject
* @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
* @return {Object} object representation of an instance
*/
toObject: function(propertiesToInclude) {
return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), {
styles: clone(this.styles)
});
}
});
/**
* Returns fabric.Textbox instance from an object representation
* @static
* @memberOf fabric.Textbox
* @param {Object} object Object to create an instance from
* @return {fabric.Textbox} instance of fabric.Textbox
*/
fabric.Textbox.fromObject = function(object) {
return new fabric.Textbox(object.text, clone(object));
};
/**
* Contains all fabric.Textbox objects that have been created
* @static
* @memberof fabric.Textbox
* @type Array
*/
fabric.Textbox.instances = [ ];
})();
+1
+1
On Sun, Sep 28, 2014 at 5:19 PM, Marcos Passos [email protected]
wrote:
+1
โ
Reply to this email directly or view it on GitHub
https://github.com/kangax/fabric.js/issues/187#issuecomment-57105904.
+1
+1 this feature
+1
+1
thanks +1, text wrapping, we need this feature!
I need this feature too, I want scaling of group across all corners, leaving the font size unchanged.
+1
+100!
+1. btw, the textbox by @charlesschaefer has been working well for me so far! nicely done!
+1!
+1
Can anyone tell me how to get only the textbox code? I mean I have fabric.js included as a separate file and I want to include all other files related to textbox functionality. I looked into the code of the demo but it has fabric.js and textbox.js embeded in one file
We can close this now? or would this mean to port word wrapping in text class?
Thanks to all who have contributed to this. I am trying to use the item as mentioned by @charlesschaefer above. The only problem that I face is the textAlign does not seem to work. It aligns the multiline text on the text element but does not position the x of the text item within the textbox component. The setting of the value of x i.e. left is showing properly but the textObject does not actually move. Any help will be appreciated.
This thread discussion has been a big help to me. I posted a SO Question here. Can anyone provide any input on the TextBox shape mentioned in the question? fabricjs is great btw. Thanks
I agree with the original poster. The font size should dictate the, uh, font size. The bounding box should not dictate the font size, otherwise, what is the font size property for.
that happens because people use a control to scale the object and they expect just the bounding box to grow.
no, that corner is for scaling, and is gonna zoom the text.
then someone created the textbox to have this behaviour.
so this is available now.
+1
After looking alot to this thread i can't understand do we have a way to resize a rectangle from a group with text without scaling text ? i tried with the above text box solutions and the best i got is the text is not scaling on X but does scale on Y.
This worked out very well for me. It utilizes the canvas' ability to measure the text width and height and tries both word breaking and shrinking the text.
It's not the most efficient thing to do but for this use case I think it's alright:
iText.on('changed',adjustTextSize.bind(.....));
...
var adjustTextSize = function (text, container, horizontalPadding, verticalPadding, canvas, enlarge) {
var scaleX = container.getScaleX ? container.getScaleX() : 1;
var scaleY = container.getScaleY ? container.getScaleY() : 1;
var containerWidth = container.getWidth() / scaleX;
var containerHeight = container.getHeight() / scaleY;
var maxWidth = containerWidth * horizontalPadding;
var maxHeight = containerHeight * verticalPadding;
var originalFontSize = text.getFontSize();
var originalText = text.text;
// Try breaking first
var afterBreakSize = workBreakStrategy(maxWidth, maxHeight);
restoreTextState(originalFontSize, originalText);
// The try shrink without break
var afterShrinkSize = shrinkStrategy(maxWidth, maxHeight);
// Use the larger font size-producing strategy
if (afterBreakSize > afterShrinkSize){
restoreTextState(originalFontSize, originalText);
workBreakStrategy(maxWidth, maxHeight);
}
if (enlarge) {
while (text.width < maxWidth && text.height < maxHeight) {
text.setFontSize(text.getFontSize() + 1)
}
}
canvas.renderAll();
function restoreTextState(originalFontSize,originalText){
text.setText(originalText);
text.setFontSize(originalFontSize);
}
function workBreakStrategy(maxWidth, maxHeight){
while (text.width > maxWidth){
var words = text.text.split(" ");
var lastWord = words.pop();
if (words.length > 0)
text.setText(words.join(" ") + '\n' + lastWord);
if (words.length > 0) continue;
text.setFontSize(text.getFontSize() - 1);
}
while (text.height > maxHeight){
text.setFontSize(text.getFontSize() - 1);
}
return text.getFontSize();
}
function shrinkStrategy(maxWidth, maxHeight){
while (text.width > maxWidth || text.height > maxHeight){
text.setFontSize(text.getFontSize() - 1);
}
return text.getFontSize();
}
};
Did anyone noticed that there is now a nearly fully working textbox class in fabricJS?
var txt = new fabric.Textbox('hello world, i'm textbox', { options... });
Nice, but I prefer not to use an RC version...
both 1.5.0 and 1.6.0RC have theyr bugs.
Just different from version to version, but any version have its own.
Textbox came out in 1.5.0 and had quite some. anyway 1.6.0rc has fixed many.
Check the draft changelog: https://github.com/kangax/fabric.js/wiki/Changelog-(draft)
do we still need this "possible feature" when we have textbox that can have editable = false and act like a text?
I don't think so, so closing
Is there an option to add more functionality for Textbox?
Basically, in our project, we try to implement Element with behavior similar to InDesign Text element.
Anytime, when you enter a text, text wraps inside its parent container, but width and height of that container stay consistent until you will resize it by yourself; a text, which is out of bounders, become invisible (similar to CSS Overflow: Hidden behavior).
no there is nothing like that in fabricjs.
yes you can custom code and extend textbox to handle that part
i have implemented that will share with you tomorrow
On Fri, 15 Sep 2017 at 2:30 AM, Andrea Bogazzi notifications@github.com
wrote:
no there is nothing like that in fabricjs.
โ
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/kangax/fabric.js/issues/187#issuecomment-329614642,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABrGnsOaSvNamgCCHi7-sACZdZJGliAlks5siZr1gaJpZM4ADVNP
.>
Fahad Nawaz
Tnx @fahadnabbasi I'll wait for it...
๐
but you should be a good developer to understand n integrate it as there
are a few functions you need to utilize in order to make it work like you
want
it tries to reduce the font size to make it fit inside the container and
after reaching a certain font size limit it clips the characters going out
On Fri, 15 Sep 2017 at 2:51 AM, Eugene Vilder notifications@github.com
wrote:
Tnx @fahadnabbasi https://github.com/fahadnabbasi I'll wait for it...
๐โ
You are receiving this because you were mentioned.Reply to this email directly, view it on GitHub
https://github.com/kangax/fabric.js/issues/187#issuecomment-329618985,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABrGnigGL7Sj1m_7_QA9DHFaMhiFOX_Uks5siZ_EgaJpZM4ADVNP
.>
Fahad Nawaz
no problem at all... :) I have some experience in JS
good give me a reminder after 13 hours if possible
On Fri, 15 Sep 2017 at 2:56 AM, Eugene Vilder notifications@github.com
wrote:
no problem at all... :) I have some experience in JS
โ
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/kangax/fabric.js/issues/187#issuecomment-329620001,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABrGniumAMS_LQMq6ebw6gKFGhjGvryrks5siaDugaJpZM4ADVNP
.>
Fahad Nawaz
Farad, could you please post your solution?
Best regards,
Eugene Vilder
On Sep 14, 2017, at 2:57 PM, fahadnabbasi notifications@github.com wrote:
good give me a reminder after 13 hours if possible
On Fri, 15 Sep 2017 at 2:56 AM, Eugene Vilder notifications@github.com
wrote:no problem at all... :) I have some experience in JS
โ
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/kangax/fabric.js/issues/187#issuecomment-329620001,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABrGniumAMS_LQMq6ebw6gKFGhjGvryrks5siaDugaJpZM4ADVNP
.>
Fahad Nawaz
โ
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.
ok so here is what you need to do
create a new js file with following code in it (this will make it to keep the width and height of the container and also enable all controls
`(function() {
fabric.Textbox.getTextboxControlVisibility = function() {
return {
tl: true,
tr: true,
br: true,
bl: true,
ml: false,
mt: false,
mr: false,
mb: false,
mtr: true
};
}
}).call(this);
(function() {
//Override function for textbox to handle the size down and height based on restrictions
fabric.Textbox.prototype._initDimensions = function(ctx) {
if (this.__skipDimension) {
return;
}
if (!ctx) {
ctx = fabric.util.createCanvasElement().getContext('2d');
this._setTextStyles(ctx);
}
// clear dynamicMinWidth as it will be different after we re-wrap line
this.dynamicMinWidth = 0;
// wrap lines
this._textLines = this._splitTextIntoLines(ctx);
// clear cache and re-calculate height
this._clearCache();
this.minHeight = this._getTextHeight(ctx);
if(this.height <= 0)
this.height=this.minHeight;
}
}).call(this);`
Now create another file and add this into it
`(function() {
fabric.Canvas.prototype._performTransformAction = function (e, transform, pointer) {
var x = pointer.x,
y = pointer.y,
target = transform.target,
action = transform.action,
actionPerformed = false;
e.pointer = pointer;
e.transform = transform;
if (action === 'rotate') {
e.transform.lastAngle = target.get('angle');
(actionPerformed = this._rotateObject(x, y)) && this._fire('rotating', target, e);
}
else if (action === 'scale') {
target.isScaling = true;
(actionPerformed = this._onScale(e, transform, x, y)) && this._fire('scaling', target, e);
}
else if (action === 'scaleX') {
(actionPerformed = this._scaleObject(x, y, 'x')) && this._fire('scaling', target, e);
}
else if (action === 'scaleY') {
(actionPerformed = this._scaleObject(x, y, 'y')) && this._fire('scaling', target, e);
}
else if (action === 'skewX') {
(actionPerformed = this._skewObject(x, y, 'x')) && this._fire('skewing', target, e);
}
else if (action === 'skewY') {
(actionPerformed = this._skewObject(x, y, 'y')) && this._fire('skewing', target, e);
}
else {
actionPerformed = this._translateObject(x, y);
if (actionPerformed) {
this._fire('moving', target, e);
this.setCursor(target.moveCursor || this.moveCursor);
}
}
transform.actionPerformed = actionPerformed;
}
}).call(this);
(function() {
fabric.Canvas.prototype._setObjectScale = function (localMouse, transform, lockScalingX, lockScalingY, by, lockScalingFlip, _dim) {
var target = transform.target, forbidScalingX = false, forbidScalingY = false, scaled = false,
changeX, changeY, scaleX, scaleY;
var t = transform.target;
if (t instanceof fabric.Textbox) {
transform.newScaleX = t.scaleX;
transform.newScaleY = t.scaleY;
transform.scaleX = t.get('scaleX');
transform.scaleY = t.get('scaleY');
transform.oldLeft = t.get('left');
transform.oldHeight = t.get('height');
transform.oldWidth = t.get('width');
transform.oldTop = t.get('top');
t.oldWidth = transform.oldWidth;
t.oldHeight = transform.oldHeight;
var h = t.height * ((localMouse.y / transform.scaleY) / (t.height + t.strokeWidth));
t.isScaling = true;
var w = t.width * ((localMouse.x / transform.scaleX) / (t.width + t.strokeWidth));
if (w >= t.minWidth) {
// t.set('width', w);
}
//t.set('width', w);
// t.set('height', h);
if (h >= t.minHeight) {
//t.set('height', h);
}
else {
// t.set('height', t.minHeight);
}
if (h < 10)
h = 10;
t.set('width', w);
t.set('height', h);
return true;
}
else {
scaleX = localMouse.x * target.scaleX / _dim.x;
scaleY = localMouse.y * target.scaleY / _dim.y;
changeX = target.scaleX !== scaleX;
changeY = target.scaleY !== scaleY;
if (lockScalingFlip && scaleX <= 0 && scaleX < target.scaleX) {
forbidScalingX = true;
}
if (lockScalingFlip && scaleY <= 0 && scaleY < target.scaleY) {
forbidScalingY = true;
}
if (by === 'equally' && !lockScalingX && !lockScalingY) {
transform.scaleX = target.get('scaleX');
transform.scaleY = target.get('scaleY');
transform.oldLeft = target.get('left');
transform.oldTop = target.get('top');
forbidScalingX || forbidScalingY || (scaled = this._scaleObjectEqually(localMouse, target, transform, _dim));
}
else if (!by) {
transform.scaleX = target.get('scaleX');
transform.scaleY = target.get('scaleY');
transform.oldLeft = target.get('left');
transform.oldTop = target.get('top');
forbidScalingX || lockScalingX || (target.set('scaleX', scaleX) && (scaled = scaled || changeX));
forbidScalingY || lockScalingY || (target.set('scaleY', scaleY) && (scaled = scaled || changeY));
}
else if (by === 'x' && !target.get('lockUniScaling')) {
transform.scaleX = target.get('scaleX');
transform.oldLeft = target.get('left');
transform.oldTop = target.get('top');
forbidScalingX || lockScalingX || (target.set('scaleX', scaleX) && (scaled = scaled || changeX));
}
else if (by === 'y' && !target.get('lockUniScaling')) {
transform.scaleY = target.get('scaleY');
transform.oldLeft = target.get('left');
transform.oldTop = target.get('top');
forbidScalingY || lockScalingY || (target.set('scaleY', scaleY) && (scaled = scaled || changeY));
}
transform.newScaleX = scaleX;
transform.newScaleY = scaleY;
forbidScalingX || forbidScalingY || this._flipObject(transform, by);
}
return scaled;
}
}).call(this);`
Now there are two functions in your main controller JS file that will resize down and trim the additional text, you can use them wherever you need them but you need to change the following functions to adjust for your needs
`main.sizeDownTextNew = function(canvasObj, limit_height)
{
if(typeof (canvasObj) == "undefined" || canvasObj == null)
return;
var counter = 250; //Maximum of 10 iterations to size down
var initial_height = limit_height;
//Grab all the different sizes inside a single text object
var sizeArray = [];
$.each(canvasObj.styles, function(i,obj){
if(typeof(obj) !="undefined" && obj != null) {
$.each(obj, function (j, style) {
if (typeof(style) != "undefined" && style != null)
if (typeof(style.fontSize) != "undefined") {
var size = Math.round(style.fontSize * 100) / 100;
sizeArray.push(size)
}
});
}
});
//Remove the duplicate text sizes
var sizeArrayUnique = new Array();
$(sizeArray).each(function(index, item) {
if ($.inArray(item, sizeArrayUnique) == -1)
sizeArrayUnique.push(item);
});
if(sizeArrayUnique.length == 0)
sizeArrayUnique.push(canvasObj.get('fontSize'));
//Which size is max
var max = Math.max.apply(Math,sizeArrayUnique);
var min = 6; //This is our minimum size
var foundmin=0; //Flag if minimum is met
while((canvasObj.height+2<canvasObj._getTextHeight())) //While the height of text is larger then actual text, try reducing the lineHeight
{
canvasObj.set('fontSize',max)
if(canvasObj.lineHeight > 1)
{
canvasObj.lineHeight = parseFloat(parseFloat(canvasObj.lineHeight)-0.05);
canvasObj.canvas.renderAll();
canvasObj.height = initial_height
}
else
{
break;
}
}/**/
//Now reduce the size of actual text font size property
while(canvasObj.height+2<canvasObj._getTextHeight()) //While the height of text is larger then actual text, try reducing the lineHeight
{
var fontSize = parseFloat(canvasObj.get('fontSize'));
fontSize=fontSize - fontSize/max; //reduce the size with a set ratio
if(fontSize > min) //If main font is greater then min font, we will now reduce
{
canvasObj.set('fontSize', fontSize);
canvasObj.height = initial_height
if(typeof(canvasObj.styles)!="undefined") //If we have multiple font sizes defined inside a text obj
{
//traverse and reduce the sizes one by one
$.each(canvasObj.styles, function(i,obj){
if(typeof(obj) !="undefined") {
$.each(obj, function (j, style) {
if (typeof(style) != "undefined")
if (typeof(style.fontSize) != "undefined" && foundmin == 0) {
//sizeArray.push(parseFloat(style.fontSize))
var fontSize2 = parseFloat(style.fontSize);
fontSize2 = fontSize2 - fontSize2 / max;
fontSize2 = Math.round(fontSize2 * 100) / 100;
if (fontSize2 > min) {
style.fontSize = fontSize2;
canvasObj.height = initial_height
}
else {
foundmin = 1;
}
}
});
}
});
}
}
else
{
break;
}
canvasObj._splitTextIntoLines(); //Internal fabric function to split lines
canvasObj.canvas.renderAll();
canvasObj.height = initial_height;
canvasObj.setCoords();
canvasObj.canvas.renderAll();
if(counter == 0) //Failsafe to exit the while loop if no condition is met
break;
}
//Now if we have resized and still some text is outside, increase the height of box as user will have to manuall delete text to make the box smaller
if(canvasObj.height < canvasObj._getTextHeight())
canvasObj.height = canvasObj._getTextHeight();
canvasObj.setCoords();
canvasObj.canvas.renderAll();
main.trimDownText(canvasObj); //Now if still some text is outside the boundry of box, we need to remove it.
main.populateProperties();
return max;
}`
/**
* Trims the additional text that goes beyond the boundry of text element
*
* @param canvasObj Actual object on which the locks are to be applied
*/
main.trimDownText = function(canvasObj)
{
var initial_height = canvasObj.height;
if(canvasObj.height+2<canvasObj._getTextHeight())
{
for(i=canvasObj._textLines.length -1; i>=0; i--)
{
var oldText = canvasObj._textLines[i];
var text = canvasObj.text;
canvasObj._textLines[i] = '';
canvasObj.__lineHeights[i] = 0.000001;
//canvasObj.canvas.renderAll();
text = text.substring(0,text.lastIndexOf(oldText)) + '';
text = main.trimLastLine(canvasObj);
canvasObj.set('text',text);
canvasObj.setCoords();
canvasObj.canvas.renderAll();
canvasObj.height = initial_height;
if((canvasObj.height+2)<canvasObj._getTextHeight())
{
continue;
}
else
{
main.canvas.deactivateAllWithDispatch();
main.canvas.renderAll();
main.canvas.setActiveObject(canvasObj); //Make it selected by default
//canvasObj.enterEditing();
canvasObj.canvas.renderAll();
break;
}
}
}
}
``
@redlive
Thank you @fahadnabbasi !!!
@fahadnabbasi What is _main_ in your case? Is it Textbox instance?
no main is the fabric js interface i am using
On Fri, 15 Sep 2017 at 8:42 PM, Eugene Vilder notifications@github.com
wrote:
@fahadnabbasi https://github.com/fahadnabbasi What is main in your
case? Is it Textbox instance?โ
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/kangax/fabric.js/issues/187#issuecomment-329819795,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABrGnvcbzZH8MMNMHALRzo-3BtR1G80Kks5siprPgaJpZM4ADVNP
.>
Fahad Nawaz
Could you please send an example of how you call main.sizeDownTextNew function?
On Sep 15, 2017, at 10:16 AM, fahadnabbasi notifications@github.com wrote:
no main is the fabric js interface i am using
On Fri, 15 Sep 2017 at 8:42 PM, Eugene Vilder notifications@github.com
wrote:@fahadnabbasi https://github.com/fahadnabbasi What is main in your
case? Is it Textbox instance?โ
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/kangax/fabric.js/issues/187#issuecomment-329819795,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABrGnvcbzZH8MMNMHALRzo-3BtR1G80Kks5siprPgaJpZM4ADVNP
.>
Fahad Nawaz
โ
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub https://github.com/kangax/fabric.js/issues/187#issuecomment-329843596, or mute the thread https://github.com/notifications/unsubscribe-auth/AFo2OiVLgdEcPXFEVRT5zMEzHTjAbfOhks5sirDYgaJpZM4ADVNP.
I implemented FixedTextbox which resizes font fit to the Box.
https://github.com/darron1217/fabric-addon
If anyone interested in this issue, please check the repo and give a PR to make it better :)