Fabric js how to add text on image - javascript

I need to add text on img object
my code
$("#c2").click(function(){
fabric.Image.fromURL('/some/link/free_circle_2.png', function(img){
img.setWidth(50);
img.setHeight(50);
canvas.add(img);
});
});
after adding image i need add number centered of this image.
I know i need to change canvas.add(img); to canvas.sendToBack(img); but how to add text keeping the position of the image

To keep the position of your text, based upon the position of your image, you should use a Group, like this:
var group = new fabric.Group([img, text], {
left: 150,
top: 100,
});
Then to place your text in the middle of your image, since the position of your text its relative to the group, you should do something like this:
text.set("top", (img.getBoundingRect().height / 2) - (text.width / 2));
text.set("left", (img.getBoundingRect().width / 2) - (text.height / 2));
Where getBoundingRect returns coordinates of object's bounding rectangle (left, top, width, height)
then,
Try yourself the following runnable example if it meets your needs:
var canvas = new fabric.Canvas('c');
$("#c2").click(function() {
fabric.Image.fromURL('https://upload.wikimedia.org/wikipedia/commons/2/25/Herma_of_Plato_-_0049MC.jpg', function(img) {
img.setWidth(100);
img.setHeight(100);
var text = new fabric.Text("01", {
fontFamily: 'Comic Sans',
fontSize: 20
});
text.set("top", (img.getBoundingRectHeight() / 2) - (text.width / 2));
text.set("left", (img.getBoundingRectWidth() / 2) - (text.height / 2));
var group = new fabric.Group([img, text], {
left: 100,
top: 25,
});
canvas.add(group);
});
});
canvas {
border: 1px dashed #333;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.4/fabric.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id='c' width=300 height=150 '></canvas>
<br>
<button id='c2'>ok</button>

Related

Animate drawing of path on HTML5 canvas

My problem is how to animate the drawing of a path between two points.
Consider a curved line or path between two points, A & B. I can draw this easily on the canvas using the line drawing functions in Konvajs.
However, what I actually want is to animate the revealing of the line so that it starts from point A and progressively draws to point B. The reveal should be animated so I can apply pleasing easings.
As a comparable example, see the brief video on this site https://coggle.it/ where the video shows the creation of a new box and the line draws to connect it to the old.
Here is a potential answer (special thanks to #markov00 re same technique in SVG). It works by manipulating the path dashOffset and dash attributes. There is an excellent explanation of the technique here in a post by Jake Archibald which also includes an interactive experiment with sliders which I found very useful.
I have tried to make the demo as lightweight as possible and just show the technique - though I added a slider and some UI to help understand the process. The use of jquery is only for the UI parts which are not needed for the technique.
Couple of points:
The demo here uses a path from 3 straight line segments. But I tried curves and combination paths and the technique works in those cases too - so any path should work.
I found that using a close-path command (z) on the path caused the path length function to be short on the true distance. This appears as an amount of the path remaining stroked or gapped at either end, with the size depending on the jump between first & last to close the path.
The path length is virtually always going to be decimal so don't try to do everything as integers as you will ultimately find your stroke is slightly overlong or short.
To adopt this for animation and easing etc, take the couple of lines from the slider change event and stick them inside the frame callback, manipulating the maths to suit your case.
// Set up the canvas / stage
var stage = new Konva.Stage({container: 'container1', width: 320, height: 180});
// Add a layer
var layer = new Konva.Layer({draggable: false});
stage.add(layer);
// show where the start of the path is.
var circle = new Konva.Circle({
x: 66,
y: 15,
radius: 5,
stroke: 'red'
})
layer.add(circle);
// draw a path.
var path = new Konva.Path({
x: 0,
y: 0,
data: 'M66 15 L75 100 L225 120 L100 17 L66 15',
stroke: 'green'
});
// get the path length and set this as the dash and dashOffset.
var pathLen = path.getLength();
path.dashOffset(pathLen);
path.dash([pathLen]);
layer.add(path)
stage.draw();
// Some UI bits
$('#dist').attr('max', parseInt(pathLen)); // set slider max to length of path
$('#pathLen').html('Path : ' + pathLen); // display path length
// jquery event listener on slider change
$('#dist').on('input', function(){
// compute the new dash lenth as original path length - current slider value.
// Means that dashLen initially = path len and moves toward zero as slider val increases.
var dashLen = pathLen - $(this).val();;
path.dashOffset(dashLen); // set new value
layer.draw(); // refresh the layer to see effect
// update the UI elements
$('#dashLen').html('Dash: ' + dashLen);
$('#pathPC').html(parseInt(100-(100 * (dashLen/pathLen)), 10) + '%');
})
.info
{
padding-left: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.js"></script>
<div class="slidecontainer">
<input class='slider' id='dist' type="range" min="0" max="100" value="0" class="slider" id="myRange"/>
<span class='info' id='pathPC'></span>
<span class='info' id='pathLen'></span>
<span class='info' id='dashLen'></span>
</div>
<div id='container1' style="display: inline-block; width: 300px, height: 200px; background-color: silver; overflow: hidden; position: relative;"></div>
<div id='img'></div>
My solution with animation:
var width = window.innerWidth;
var height = window.innerHeight;
// Set up the canvas / stage
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
// Add a layer
var layer = new Konva.Layer({
draggable: false
});
stage.add(layer);
// show where the start of the path is.
var circle = new Konva.Circle({
x: 66,
y: 15,
radius: 5,
stroke: 'red'
})
layer.add(circle);
// draw a path.
var path = new Konva.Path({
x: 0,
y: 0,
data: 'M66 15 L75 100 L225 120 L100 17 L66 15',
stroke: 'green'
});
// get the path length and set this as the dash and dashOffset.
var pathLen = path.getLength();
path.dashOffset(pathLen);
path.dash([pathLen]);
// make some animation with stop
var anim = new Konva.Animation(function (frame) {
var dashLen = pathLen - frame.time / 5;
path.dashOffset(dashLen);
if (dashLen < 0) {
anim.stop();
}
}, layer);
anim.start();
layer.add(path)
stage.draw();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.js"></script>
<div id='container' style="display: inline-block; width: 300px, height: 200px; background-color: silver; overflow: hidden; position: relative;"></div>
<div id='img'></div>
And here is an alternative to #Roxane's animated version but using a tween.
var width = window.innerWidth;
var height = window.innerHeight;
// Set up the canvas / stage
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
// Add a layer
var layer = new Konva.Layer({
draggable: false
});
stage.add(layer);
// show where the start of the path is.
var circle = new Konva.Circle({
x: 66,
y: 15,
radius: 5,
stroke: 'red'
})
layer.add(circle);
// draw a path.
var path = new Konva.Path({
x: 0,
y: 0,
data: 'M66 15 L75 100 L225 120 L100 17 L66 15',
stroke: 'green'
});
// get the path length and set this as the dash and dashOffset.
var pathLen = path.getLength();
path.dashOffset(pathLen);
path.dash([pathLen]);
layer.add(path); // have to add to layer for tweening.
// create the tween
var tween = new Konva.Tween({
node: path,
dashOffset: 0,
easing: Konva.Easings['BounceEaseOut'],
duration: 1.5
});
tween.play(); // execute the tween
stage.draw();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.js"></script>
<div id='container' style="display: inline-block; width: 300px, height: 200px; background-color: silver; overflow: hidden; position: relative;"></div>
<div id='img'></div>

json.stringify: After resizing of an object height and width properties are not updated

Next code create triangles on canvas, if you are not clicking over an existing triangle. And you may save all elements to JSON with clicking on "save" button.Then preview the JSON variable in console.
My problem is that after resizing of an object, height and width are not updated in Json, when clicked on save again.
To resize the triangle You have to click on it. Then grab one of the anchor rectangles and drag it from the center of triangle.
What action must I perform to have also new height and width of the object?
Next is code snippet on jsFfidle: https://jsfiddle.net/ug7p9myc/76/
var canvas = new fabric.Canvas('c5');
function some1(x1, y2) {
var c = new fabric.Triangle({
left: posX2,
top: posY2,
width: 15,
height: 25,
strokeWidth: 3,
fill: '#666',
stroke: '#666'
});
canvas.add(c);
}
var posX2, posY2;
canvas.on('mouse:down', function(e) {
var pointer = canvas.getPointer(e.e);
posX2 = pointer.x;
posY2 = pointer.y;
if (e.target) {} else {
some1(posY2, posY2);
}
});
function saveJsonF() {
var jsonToPHP = JSON.stringify(canvas.toObject());
console.log(jsonToPHP);
}
document.getElementById("saveJsonID").onclick = saveJsonF;
<script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.22/fabric.min.js"></script>
<input type="button" class="Btn1" id="saveJsonID" value="Save" /></br><br>
<canvas id="c5" width="1060" height="550" style="border: 1px solid black"></canvas>
Use getScaledWidth and getScaledHeight to get the transformed values. If you scale an object, its scaleX and scaleY value changes.

Drag object programmatically

I have 3 rectangles on top of eachother like so:
new Fabric.Rect({
width: 200 - index * 30,
height: 20,
hasBorders: false,
selectable: false,
hasControls: false
});
and then I have a click event which detects clicks NEAR (not on the rectangle) the rectangles and makes the top rectangle (highest one of the stack of 3) selectable (to drag it):
var first = this.first().shape; // Fabric.Rect
canvas.setActiveObject(first);
However, this does not set the object on the cursor, to drag the object.
How can I make it so the object is selected, immediately moved to the cursor and enabled to be dragged around once the click event fires?
This should get you fairly close, if I understood you correctly.
Click anywhere inside the black square of the canvas and outside the red object.
var canvas = new fabric.Canvas('c', {
selection: false,
});
var rectangle = new fabric.Rect({
fill: 'red',
left: 10,
top: 10,
width: 100,
height: 100 //,
//padding: 50
});
canvas.on('mouse:down', function(env) {
var x = env.e.offsetX;
var y = env.e.offsetY;
rectangle.setLeft(x - rectangle.width / 2);
rectangle.setTop(y - rectangle.height / 2);
canvas.setActiveObject(rectangle);
rectangle.setCoords();
canvas.renderAll();
canvas.on('mouse:move', function(env) {
var x = env.e.offsetX;
var y = env.e.offsetY;
rectangle.setLeft(x - rectangle.width / 2);
rectangle.setTop(y - rectangle.height / 2);
rectangle.setCoords();
canvas.renderAll();
});
canvas.on('mouse:up', function(env) {
canvas.off('mouse:move');
});
});
canvas.add(rectangle);
canvas.renderAll();
#c {
border: 1px solid black;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/fabric.js/1.4.0/fabric.min.js"></script>
<canvas id="c" width="200" height="200"></canvas>
I intentionally commented out padding on the rectangle, but left it in the code, in case you wanted to use that as your NEAR logic instead of what you already have. If you do choose to use padding as your NEAR logic you will then need to change the on canvas mouse:down event to an on canvas object:selected event.
Also, if you haven't done so already, you might like to take a close look at this Objects Bounding Rectangles example for some further ideas for your NEAR logic, http://fabricjs.com/bounding-rectangle.
Any-who, let me know how you get on, matey!

Set two canvas squares to the center of a canvas with HTML5

I have two boxes positioned on a canvas that I am trying to center. You can view this on JS fiddle: http://jsfiddle.net/FVU47/5/
My canvas has 1000 height and 1000 width as follows:
<canvas id="myCanvas" width="1000" height="1000" style="border:3px solid #385D8A; outline:1px solid #7592B5; margin-left: 0px; margin-top: 0px; background-color: #B9CDE5"></canvas>
I am then attempting to center the two boxes with the following code, which would place either box1 or box2 in the center of the canvas, depending on whether I click on "Go to Box 1" or "Go to Box 2" (see the bottom of the JSFiddle Result quadrant:
$(document).ready(function(){
$("#box1click").click(function(){
if (rect1.x <= 500) {
positionWidthSet = Math.abs(rect1.x - canvas.width/2) + rect1.x;
}
else{
positionWidthSet = Math.abs(Math.abs(rect1.x - canvas.width/2) + rect1.x)
}
if (rect1.y >= 500) {
positionHeightSet = Math.abs(rect1.y -canvas.height/2);
}
else{
positionHeightSet = Math.abs(Math.abs(rect1.y - canvas.height/2) + rect1.y);
}
positionCanvasContext(positionWidthSet,positionHeightSet);
});
});
$(document).ready(function(){
$("#box2click").click(function(){
if (rect2.x <= 500) {
positionWidthSet = Math.abs(rect2.x - canvas.width/2) + rect2.x;
}
else{
positionWidthSet = Math.abs(Math.abs(rect2.x - canvas.width/2) + rect2.x)
}
if (rect2.y >= 500) {
positionHeightSet = Math.abs(rect2.y -canvas.height/2);
}
else{
positionHeightSet = Math.abs(Math.abs(rect2.y - canvas.height/2) + rect2.y);
}
positionCanvasContext(positionWidthSet,positionHeightSet);
});
});
Currently, clicking on either "Go to Box 1" or "Go to Box 2" does not center the canvas around either Box 1 or Box 2, even though experimenting with my formulas in the console would seem to indicate otherwise.
Here's one way: http://jsfiddle.net/m1erickson/GpMsk/
Put all your rects in an array:
var rects=[];
rects.push({
x: 103,
y: 262,
w: 200,
h: 100,
fillStyle: 'red',
hovered: false
});
rects.push({
x: 484,
y: 170,
w: 200,
h: 100,
fillStyle: 'blue',
hovered: false
});
Create a draw() function that draws all rects in the rects[] array
Draw all rects with a specified x/y offset to their original x/y:
function draw(offsetX,offsetY){
ctx.clearRect(0,0,canvas.width,canvas.height);
for(var i=0;i<rects.length;i++){
var r=rects[i];
ctx.fillStyle=r.fillStyle;
ctx.fillRect(r.x+offsetX,r.y+offsetY,r.w,r.h);
}
}
When you click a button, calculate the offsets necessary to pull the specified rectangle to the center of the canvas
Then redraw all rects with the calculated offsets.
var centerX=canvas.width/2;
var centerY=canvas.height/2;
function centerThisRect(rectsIndex){
var r=rects[rectsIndex];
var offsetX=centerX-r.x-r.w/2;
var offsetY=centerY-r.y-r.h/2;
draw(offsetX,offsetY);
}

How to calculate rotationDegree for fillPatternImage of wedges?

I have the following circle, made of wedges. Each wedge has a fillPatternImage. Each fillPatternImage has an image with a centered text. The text varies in length for each wedge.
The problem is I need to give each fillPatternImage a rotation degree so all text aligns perfectly along the circle’s perimeter. But I have no clue what rotation degree to give each fillPatternImage.
For example, when I give all fillPatternImages a rotation degree of 95°, short texts align well, but long texts turn out crooked.
I suppose rotation degree must be dynamic for each fillPatternImage. But I haven’t found out how. I have the wedge dimensions, the text width, etc.
Code example for fillPatternImage:
imageObj.src = Caption + ".png";
imageObj.onload = function () {
nts.setFillPatternImage(imageObj);
nts.setFillPatternRotationDeg(95);
// nts.setFillPatternOffset(-220, 100);
nts.setFillPatternX(radio - 15);
nts.setFillPatternY(leftSide);
nts.setFillPatternRepeat("no-repeat");
nts.draw();
}
Any ideas would be of great help. Thanks in advance.
How to calculate fillPatternRotation angle for a Kinetic Wedge
Your text will always be appropriately rotated in the Kinetic wedge with this fillPatternRotation:
fillPatternRotation:Math.PI/2+wedgeAngleDeg*Math.PI/360
Where wedgeAngleDeg is the value supplied to wedge.angleDeg:
angleDeg: wedgeAngleDeg
You also need to set the appropriate wedge fillPatternOffset to match your text-image
You must horizontally offset the fillPattern to the midpoint of the text on the image
Assuming your text is horizontally centered on your image, that offset is image.width/2.
fillPatternOffset X is image.width/2
You must vertically offset the fillPattern to align the image text to the largest width on the wedge.
You can calculate the largest wedge width like this:
var textTop=radius*Math.sin(wedgeAngleDeg*Math.PI/180);
So your fillPatternOffset becomes:
fillPatternOffset: [image.width/2, image.height-textTop],
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/Y53cH/
<!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.6.0.min.js"></script>
<script defer="defer">
var stage = new Kinetic.Stage({
container: 'container',
width: 350,
height: 350
});
var img=new Image();
img.onload=function(){
start();
}
img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/testing.png";
function start(){
var layer = new Kinetic.Layer();
var wedgeAngleDeg=60;
var radius=100;
var textTop=radius*Math.sin(wedgeAngleDeg*Math.PI/180);
var wedge = new Kinetic.Wedge({
x: stage.getWidth() / 2,
y: stage.getHeight() / 2,
radius: radius,
angleDeg: wedgeAngleDeg,
fillPatternImage: img,
fillPatternOffset: [img.width/2, img.height-textTop],
fillPatternRepeat:"no-repeat",
fillPatternRotation:Math.PI/2+wedgeAngleDeg*Math.PI/360,
stroke: 'black',
strokeWidth: 4,
rotationDeg: 0
});
layer.add(wedge);
wedge.setRotationDeg(-45);
// add the layer to the stage
stage.add(layer);
}
</script>
</body>
</html>

Categories