Fabricjs Textbox make the text shrink to fit - javascript

I would define a text box (single-line)
By default, with a character size of 16 (for example)
When the text gets bigger than the text box, I do not want it to wrap or the text box gets bigger, I want the text size to fit the maximum size of the text box. text. When the text returns to a smaller size, it can resume its initial size (in our example 16). Possibly manage a minimum size
If you have an idea, I take :)
thanks in advance
Test Case : http://jsfiddle.net/Da7SP/273/
// initialize fabric canvas and assign to global windows object for debug
var canvas = window._canvas = new fabric.Canvas('c');
// ADD YOUR CODE HERE
var canvas = window._canvas = new fabric.Canvas('c');
var t1 = new fabric.Textbox('My Text', {
width: 200,
top: 5,
left: 5,
fontSize: 16,
textAlign: 'center'
});
var t2 = new fabric.Textbox('My text is longer, but I do not want the box to grow, or the text to wrap. I only want the text to fit the available size', {
width: 200,
height: 200,
top: 250,
left: 5,
fontSize: 16,
textAlign: 'center'
});
canvas.add(t1);
canvas.add(t2);
A small video to explain what I want :
When the text gets bigger than the text box, I want the text size fit the maximum size of the text box.

This is a basic fiddle that can replicate your idea.
The point is that you have an event that fires on every text change and that can be used to do something before the textbox is rendered.
In this case i m shrinking font size based on a non standard parameter i added to textbox called fixedWidth
// ADD YOUR CODE HERE
var canvas = new fabric.Canvas('c');
var t1 = new fabric.Textbox('MyText', {
width: 150,
top: 5,
left: 5,
fontSize: 16,
textAlign: 'center',
fixedWidth: 150
});
canvas.on('text:changed', function(opt) {
var t1 = opt.target;
if (t1.width > t1.fixedWidth) {
t1.fontSize *= t1.fixedWidth / (t1.width + 1);
t1.width = t1.fixedWidth;
}
});
canvas.add(t1);
canvas {
border: 1px solid #999;
}
<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" width="600" height="600"></canvas>

Here's a similar answer to https://stackoverflow.com/a/41335051/1469525 but this example modifies the fontSize up to a maxFontSize (the initial fontSize). This allows the text to shrink and then grow back to the original size, as well as preventing line wraps.
(Note, if you use this, you'll have to find a way to prevent the user from using the enter key. I put in a check that basically cancels it if an enter key is detected. My app actually has a separate form to enter the text, so I can control prevention through normal javascript. I'm not quite sure how to do that directly on a canvas element when it is editable like here...)
var canvas = new fabric.Canvas('c');
var t1 = new fabric.Textbox('MyText', {
width: 150,
top: 5,
left: 5,
fontSize: 16,
textAlign: 'center',
maxFontSize: 16,
});
canvas.on('text:changed', function(opt) {
var t1 = opt.target;
if (t1.text.match(/[\r\n]/)) return;
t1.set({
fontSize: t1.maxFontSize,
});
while (t1._textLines.length > 1) {
t1.set({
fontSize: t1.fontSize - 1,
});
}
});
canvas.add(t1);
canvas {
border: 1px solid #999;
}
<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" width="600" height="600"></canvas>

Related

How to drag and drop items between 2 columns with "Konva.JS"?

I'm looking for a way to drag and drop items between 2 columns with Konva.JS.
Since I found a sample code using Sortable.JS, I ported it and wrote the following code. With this code, I expected there were two vertical independent scroll bars, such as in this example image:
However, there aren't, as shown in this image from running my code:
.
My code:
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/konva#5.0.2/konva.min.js"></script>
<meta charset="utf-8" />
<style>
body {
margin: 0;
padding: 0;
background-color: #f0f0f0;
height:100%;
overflow: hidden;
}
#Leftcontainer {
overflow: auto;
}
#Rightcontainer {
overflow: auto;
}
</style>
</head>
<body>
<div id="container">
<div id="Leftcontainer"></div> <!-- Left Column -->
<div id="Rightcontainer"></div> <!-- Right Column -->
</div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: 60*1090,
});
//Layer 1
var layer = new Konva.Layer();
stage.add(layer);
//Layer 2
var tempLayer = new Konva.Layer();
stage.add(tempLayer);
//Load Image (Group of Left Column)
var leftGroup = new Konva.Group({
id: 'Leftcontainer',
});
layer.add(leftGroup);
for (let i = 0; i < 100; i++) {
var imageObj = new Image();
imageObj.src = './assets/apple.jpg';
imageObj.addEventListener('load', function() {
var dragImage = new Konva.Image({
x: 5,
y: 20+(5+100)*i,
image: imageObj,
width: 100,
height: 100,
draggable: false,
});
leftGroup.add(dragImage);
layer.draw();
});
};
//Load Text (Group of Right Column)
var rightGroup = new Konva.Group({
id: 'Rightcontainer',
});
layer.add(rightGroup);
for (var i = 0; i < 100; i++) {
var WordLabel = new Konva.Label({
x: 300,
y: 18+60*i,
opacity: 0.75,
draggable: true,
fill: 'green',
});
WordLabel.add(
new Konva.Tag({
fill: 'green',
lineJoin: 'round'
})
);
WordLabel.add(
new Konva.Text({
text: "Apple",
fontFamily: 'Calibri',
fontSize: 18,
padding: 5,
fill: 'white',
})
);
rightGroup.add(WordLabel);
};
layer.draw();
</script>
</body>
</html>
It would appear that you are intending there to be two columns on your page since you have the HTML for the container, Leftcontainer and Rightcontainer. You then appear to be setting a Konva stage in the 'container' div but later creating Konva groups to co-relate to left + right containers. You then make these groups long and expect there to be vertical scrollbars.
You are making a false assumption regarding the relationship between the HTML5 canvas (for which Konva is a wrapper) and its interaction with HTML elements. The basic principle is that an HTML5 canvas 'lives' inside a single HTML element. You cannot 'share' bits of it between HTML elements in the way that you are attempting.
[Aside: Under the covers, Konva DOES create a stage per layer, but that still does not allow placing those layers into other host containers than the main stage.]
Options:
1 - you do not specifically require a canvas-based solution to provide an image-based drag & drop. You would already have found this with sortable.js. But if you are simply using this as a learning activity to understand the canvas then well done you!
2 - continuing this the canvas approach, your basic architecture of a stage and a group per column is reasonable. But you have to take care of producing the scroll bars since in the world of canvas there are no such handy UI shortcuts.
3 - again following a canvas solution, ignore the main 'container' element, and create a stage in EACH of the left and right container elements to represent your left and right columns. Draw the content, then approach the problem as one of dragging an element from one canvas to another.

How can I change the blotter canvas size?

For some reason the blotter.js canvas size is not cangable. Blotter seems to generate a canvas named b-canvas by default? But I can't find where it gets it width and height from? And I am not able to set a new width and size by .style in js. Help?!
I've tried all the techniques I could find.
I've also tried to set a Overflow: visible to the parent (Text) in js and in the css file, but the render is still crashing with the bounds of the canvas. What am I doing wrong?
var text = new Blotter.Text("Hello", {
weight: 800,
size: 80,
fill: 'lightgray',
paddingLeft: 80,
paddingRight: 80,
paddingBottom: 80,
paddingTop: 80
});
/*
* https://blotter.js.org
*/
var blotter = new Blotter(material, {
texts: text
});
var canvas = blotter.forText(text).domElement;
canvas.style.visibility = 'visible!important';
canvas.style = "width: 100vh !important";
canvas.style = "height: 100vw !important";
canvas.style = "z-index: -3 !important";
container.appendChild(canvas);
You can simply wrap it inside a div and resize that. The canvas size is probably calculated based on the font-size and it is as big as the drawing itself.
<div id="wrap">
<canvas></canvas>
</div>
<style>
#wrap canvas {
width: 50px !important
}
</style>
Take a look at the "Blotter.js" website itself and inspect its logo, see that it has this same code and how you can resize it.

fabric.js cannot get the text aligned witht the textAlign

I cannot get the textAlign working in fabric.js The fontSize, fontBackground work properly, but not textAlign. Am I missing something?
var canvas = new fabric.Canvas('myCanvas');
$('button.addText').click(function() {
var text = new fabric.Text($(this).siblings().val(), {
textAlign: 'center',
fontSize: 40
});
canvas.add(text);
});
var canvas = new fabric.Canvas('myCanvas');
var align = ["left", "center", "right" , "justify"];
var el = document.getElementById('res');
var txt = 'FabricJS \n is \nAwsome';
var text = new fabric.Text(txt, {
textAlign: 'left', //"left", "center", "right" or "justify".
width:450,
fontSize: 40
});
canvas.add(text);
changeAlign();
function changeAlign(){
var val = align[Math.floor(Math.random() * align.length)];
text.set('textAlign',val);
el.innerHTML = 'Text Alignment : ' +val;
canvas.setActiveObject(text);
canvas.renderAll();
}
canvas {
border : 2px dotted blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.17/fabric.min.js"></script>
<canvas id="myCanvas" width="600" height="300"></canvas><br>
<button onclick='changeAlign()'>Change align</button><p id='res'></p>
#AndreaBogazzi He is right. In Text, box depends on the width of first line, if you want then it will work for multi line texts . Check Snippet.
I guess the main point is that if the text is one line only, fabric will size the text bounding box to the necessary length and there will not be any alignment to do.
If the text is multiline the alignment should work.
The alignment is INSIDE the box and is not the direction where the box grows when the text grows.
It depends on the type of the object. If you have for example IText or Text the alignment will work only in the area of the canvas itself.
For example if you want to align an IText type on the center of the canvas you would use:
canvas.getActiveObject().centerV();
canvas.getActiveObject().setCoords();
canvas.renderAll();
If you want to center the text itself it would apply only to Textbox type, you would then be able to use:
canvas.getActiveObject().textAlign = "center";

interactive text box with fabricjs

I am working fabricjs textbox. I wrote the following code to change text options like font-size, font-weight, line-height.
My code is working but the problem is When I select a textbox object and change font-size or font weight or line height it does not work unless I click on canvas outside textbox.
But I want to change these value when I make change on input fields. My code are as following:
canvas = new fabric.Canvas('canvas');
// Add Text to Canvas
$('#canvasAddText').on('click', function(){
var _text = $('#canvasText').val();
var _color = $('#textColor').val();
var _fontSize = $('#fontSize').val();
var _fontWeight = $('#fontWeight').val();
var _lineHeight = $('#lineHeight').val();
var TextBox = new fabric.Textbox( _text, {
top: 30,
left: 30,
width: 200,
fill: _color,
fontSize: _fontSize,
fontWeight: _fontWeight,
lineHeight: _lineHeight,
fontFamily: 'Comic Sans',
textDecoration: 'none',
textAlign: 'left',
});
canvas.add(TextBox);
});
// Change Font Size
$('#fontSize').on('change', function (){
canvas.getActiveObject().setFontSize($(this).val());
});
// Change Font Weight
$('#fontWeight').on('change', function (){
canvas.getActiveObject().setFontWeight($(this).val());
});
// Change Line Height
$('#lineHeight').on('change', function (){
canvas.getActiveObject().setLineHeight($(this).val());
});
You should trigger object modified event and then renderAll() function to make sure changes reflect. Below is example code which you need to have for on change event of font size.
// Change Font Size
$('#fontSize').on('change', function (){
canvas.getActiveObject().setFontSize($(this).val());
canvas.trigger("object:modified",{target: canvas.getActiveObject()});
canvas.renderAll();
});

Kinetic canvas with user text field input

I've pulled out most of my hair trying to find a solution to no avail.
I know this must be easy but being a javascript neophyte I can't seem to wrap my head around it and am hoping someone can help me out!!!
I'm trying to create a simple canvas element that has an image with text (with wrapping) on top of it. The text will come from a text field outside of the canvas (nothing fancy). I've seen where the canvas populates with the text as it is input into the text field which is what I'm looking for (no submit button). Eventually, I will need to add 2 additional text fields with different information, doing the same thing but for right now I'd be happy to get one to work!
The kinetic text tutorial found here (http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-text-tutorial/ - code below) would be perfect if I could only figure out how to get the "complex text" to be pulled from a regular text input field instead of being hard coded!
Any help would be greatly appreciated!
Working code without input text field:
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.7.4.min.js"></script>
<script defer="defer">
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 220
});
var layer = new Kinetic.Layer();
var simpleText = new Kinetic.Text({
x: stage.getWidth() / 2,
y: 15,
text: 'Simple Text',
fontSize: 30,
fontFamily: 'Calibri',
fill: 'green'
});
// to align text in the middle of the screen, we can set the
// shape offset to the center of the text shape after instantiating it
simpleText.setOffset({
x: simpleText.getWidth() / 2
});
// since this text is inside of a defined area, we can center it using
// align: 'center'
var complexText = new Kinetic.Text({
x: 100,
y: 60,
text: 'COMPLEX TEXT\n\nAll the world\'s a stage, and all the men and women merely players. They have their exits and their entrances.',
fontSize: 18,
fontFamily: 'Calibri',
fill: '#555',
width: 380,
padding: 20,
align: 'center'
});
var rect = new Kinetic.Rect({
x: 100,
y: 60,
stroke: '#555',
strokeWidth: 5,
fill: '#ddd',
width: 380,
height: complexText.getHeight(),
shadowColor: 'black',
shadowBlur: 10,
shadowOffset: [10, 10],
shadowOpacity: 0.2,
cornerRadius: 10
});
// add the shapes to the layer
layer.add(simpleText);
layer.add(rect);
layer.add(complexText);
stage.add(layer);
</script>
</body>
</html>
Possible solution: to add one input field, set up event listener which fire on change of its value and then start drawing.
<body>
<div id="container"></div>
<input type="text" id='complexText' size="20"/>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.7.4.min.js"></script>
<script defer="defer">
var inputField = document.getElementById('complexText');
inputField.addEventListener('change', function() {
var inputText = this.value;
showText(inputText);
}, false);
function showText(inputText) {
*** here goes all your 'Kinetic' code ***
}
See example at jsBin.

Categories