Kinetic canvas with user text field input - javascript

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.

Related

Setting Text Background Color with Gradient Fabric Js

I am trying to set a background Color to an IText Object from Fabric.js and have encoutered two problems:
Is it possible to set a linear Gradient to the Backgroundcolor of text somehow?
Is it possible to have some padding withing the background colored Part of the text
Thanks for the answers!
I am pretty sure what you are asking is not possible, It is no where mentioned on their documentation, Here's an alternative to your problem
Instead of adding a background to text, you can create a rectangle object and add fill it with gradient, you can refer to fabric js documentation for more on gradients
http://fabricjs.com/fabric-intro-part-2#gradients
You can then add the text over the rectangle object, I have added a example
The padding inside will then just be the width and height of the rectangle which you can easily adjust.
fabric.Object.prototype.set({
transparentCorners: false,
cornerColor: 'rgba(102,153,255,0.5)',
cornerSize: 12,
padding: 5
});
// initialize fabric canvas and assign to global windows object for debug
var canvas = window._canvas = new fabric.Canvas('c1');
var text = new fabric.IText("Bharat\nasdsa\nasdasda\nnn\nklssd", {
fontFamily: 'Courier New',
left: 20,
top: 20,
fontSize: 26,
fill: '#fff'
});
var rect = new fabric.Rect({
width: 200,
height: 200,
fill: '#ffcc12',
opacity: 1
});
rect.setGradient('fill', {
x1: 0,
y1: 0,
x2: rect.width,
y2: 0,
colorStops: {
0: "red",
0.2: "orange",
0.4: "yellow",
0.6: "green",
0.8: "blue",
1: "purple"
}
});
var group = new fabric.Group([rect, text]);
canvas.add(group);
canvas.centerObject(group);
canvas {
border: 1px solid #999;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.6/fabric.min.js"></script>
<canvas id="c1" width="300" height="300"></canvas>
The solution that worked for me and allows to edit the text is the following
Group Rectangle with background as in abdulmoeez answer
Then watch if this group is selected, if it is selected un-group it and set selected element to the text, so that it is editable.
If user has finished editing the text, re-render it with fitting border and regroup

Fabricjs Textbox make the text shrink to fit

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>

kineticjs user input to change text size

I got 90% of the way through making a canvas for a mug designer with user image upload and text styling (colour, weight, font style etc..) but I couldn't get my head around a bug in my code.
now I'm using kinetic.js to make it and have a text field with keyup and want to link it to a user input to resize text, I have worked with canvas but am new to kinetics so an easy way to implement this would be helpful.
HTML:
<input type="text" id="textBox" placeholder="Your Text">
<br>Text Size:
<input type="range" id="textSize" min="4" max="100" step="1" value="30" />
js:
var text = new Kinetic.Text({
x: 20,
y: 30,
text: '',
fontSize: '30',
fontFamily: 'Calibri',
fill: 'black',
draggable: true
});
document.getElementById("textBox").addEventListener("keyup", function () {
text.setText(this.value);
layer.draw();
}, true);
var stage = new Kinetic.Stage({
container: 'container',
width: 375,
height: 200
});
var layer = new Kinetic.Layer();
layer.add(text);
stage.add(layer);
here's the full fiddle
I also need to have the "onLoad" image (of Yoda) to be inserted with the "imageLoader", so help on that would be good too. (will post as a separate question if necessary).
Any tips appreciated, thanks in advance
There are two decent options I can think of: adjusting the font size and adjusting the scale of the text element. Very similar in either case.
Font size:
document.getElementById("textSize").addEventListener("change", function() {
var size = this.value;
text.fontSize(size);
layer.draw();
}, true);
Fiddle: http://jsfiddle.net/pDW34/8/
Scale:
document.getElementById("textSize").addEventListener("change", function() {
var scale = this.value/100*4;
text.scale({x: scale, y: scale});
layer.draw();
}, true);
Fiddle: http://jsfiddle.net/pDW34/9/

Raphael-free-transform looses scale value

in the following code re-scaling of the square object goes wrong:
after clicking the button (which apply y-scale factor and make a rectangle out of our square) object displays correct BUT if you touch handles after that the previous scale values erase and square displays again.
I want to be able to make rectangle in a code and re-scale it uniformly using handles afterwards.
How to solve this problem?
<html>
<head>
<title></title>
<script src="scripts/raphael.js"></script>
<script src="scripts/raphael.free_transform.js"></script>
</head>
<body>
<button id="btn" onclick="onClick()">apply scale</button>
<script>
var r = Raphael(100, 0 , 300, 400);
var square = r.rect(100,150, 100, 100).attr({ fill: "#aaa", stroke: "black", opacity: 0.5 });
var ft = r.freeTransform(square, {}, function () {});
ft.setOpts({
attrs: { fill: 'white', stroke: '#000' },
drag: false,
keepRatio: ['axisX', 'axisY'],
size: 5,
scale: ['axisX', 'axisY'],
rotate:false
});
function onClick() {
ft.attrs.scale.y = 2;
ft.apply();
}
</script>
</body>
</html>
jsfiddle is here
When you scale y by 2, it violates keepRatio: ['axisX', 'axisY'] rule. So either you change
`keepRatio: false,`
or you scale by keeping the ratio.
edit:
You can change the aspect ratio by ft.attrs.ratio = 1/2 on button click. see updated js fiddle
To get js.fiddle working i added the line r.setViewBox(0, 0, 300, 400, true);
http://jsfiddle.net/Z2cqT/18/

How do I 'forward' (mouse) events to a node/shape on a lower Zindex in kineticjs

I'm working with kineticjs where I have a stage with one layer in it. This layer has multiple shapes on it who react on a mouseover event (they show a tooltip)
besides these shapes I also have some text nodes on a higher ZIndex (so they are above the shapes).
This all works fine with one exception, when a text is on top of a shape, the shape doesn't show a tooltip I assume it's because the textnode uses the event, rather than 'forward' it, eventhough I haven't bound anything to the text node.
How can I propagate events to nodes on a lower ZIndex?
Personally I'd create a separate layer to put anything you dont want to register mouse events on and when you create the layer set its listening property to false.
You could also set the this property for the individual elements.
Here's an example using layers...
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var normalLayer = new Kinetic.Layer();
var textLayer = new Kinetic.Layer({listening :false});
var rect = new Kinetic.Rect({
x: 20,
y: 20,
width: 100,
height: 50,
fill: 'green',
stroke: 'black',
strokeWidth: 4
});
rect.on('click', function(evt) {
this.setFill('blue');
normalLayer.draw();
});
normalLayer.add(rect);
var simpleText = new Kinetic.Text({
x: 40,
y: 40,
text: 'Simple Text',
fontSize: 30,
fontFamily: 'Calibri',
textFill: 'yellow'
});
textLayer.add(simpleText);
stage.add(normalLayer);
stage.add(textLayer);
http://jsfiddle.net/MT435/
Remember the listening property can be set for individual elements aswell.

Categories