Aligning Text Left, Center, and Right with fabricjs - javascript

I've got some text on my canvas and I'm trying to use buttons to align the text to the left, center, and right in it's my fabricjs canvas. I saw this example but would prefer to use buttons over a select box. I've tried a bunch and am feeling pretty lost.
var $ = function(id){return document.getElementById(id)};
var canvas = this.__canvas = new fabric.Canvas('c');
canvas.setHeight(300);
canvas.setWidth(300);
document.getElementById('text-align').onclick = function() {
canvas.getActiveObject().setTextAlign(this.value);
canvas.renderAll();
};
var text = new fabric.IText('Some demo\nText', {
left: 10,
top: 10,
fontSize: 22,
hasBorders: true,
hasControls: true,
cornerStyle: 'circle',
lockRotation: true,
hasControls: true,
lockUniScaling: true,
hasRotatingPoint: false,
})
canvas.add(text);
canvas {
border: 1px solid #dddddd;
margin-top: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.min.js"></script>
<button id="text-align" value="left">Left</button>
<button id="text-align" value="center">Center</button>
<button id="text-align" value="right">Right</button>
<canvas id="c"></canvas>

The issue is not in the fabric code. Rather it was because you were using the same id for all the buttons. document.getElementById returns only the first instance and therefore your click listener was getting added to the 'left' button only
var $ = function(id) {
return document.getElementById(id)
};
var canvas = this.__canvas = new fabric.Canvas('c');
canvas.setHeight(300);
canvas.setWidth(300);
document.querySelectorAll('.text-align').forEach(function(btn) {
btn.onclick = function() {
canvas.getActiveObject().setTextAlign(this.value);
canvas.renderAll();
};
})
var text = new fabric.IText('Some demo\nText', {
left: 10,
top: 10,
fontSize: 22,
hasBorders: true,
hasControls: true,
cornerStyle: 'circle',
lockRotation: true,
hasControls: true,
lockUniScaling: true,
hasRotatingPoint: false,
})
canvas.add(text);
canvas {
border: 1px solid #dddddd;
margin-top: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.min.js"></script>
<button class="text-align" value="left">Left</button>
<button class="text-align" value="center">Center</button>
<button class="text-align" value="right">Right</button>
<canvas id="c"></canvas>

Related

Resizing rectangle based on user input

I am trying to resize a rectangle based on the user input, but i get an error saying: Cannot read property 'addEventListener' of null. Do i need to change something in the HTML? I tried moving the script below the body but it didn't work. Seems like everything is alright but it doesn't work.
https://jsfiddle.net/sbLk9zvd/2/
var rect = new fabric.Rect({
fill: "red",
width: 300,
height: 400,
stroke: "gray",
strokeWidth: 30,
fill: "lightgray",
centeredRotation: true,
centeredScaling: true,
});
canvas.add(rect);
var Length = document.getElementById("Length");
var Width = document.getElementById("Width");
Length.addEventListener("input", Modify_Length);
Width.addEventListener("input", Modify_Width);
function Modify_Length() {
var rect = new fabric.Rect({
fill: "red",
width: parseFloat(Width.value) || 300,
height: 400 || parseFloat(Length.value),
stroke: "gray",
strokeWidth: 30,
fill: "lightgray",
centeredRotation: true,
centeredScaling: true,
});
}
function Modify_Width() {
var rect = new fabric.Rect({
fill: "red",
width: parseFloat(Width.value) || 300,
height: parseFloat(Length.value) || 400,
stroke: "gray",
strokeWidth: 30,
fill: "lightgray",
centeredRotation: true,
centeredScaling: true,
});
}
canvas.renderAll();
There is no need for a separate listener for each element. Just share a single callback function. Also, don't forget to rerender.
const canvas = new fabric.Canvas("c");
const rect = new fabric.Rect({
fill: "red",
width: 200,
height: 80,
stroke: "gray",
strokeWidth: 1,
fill: "lightgray",
centeredRotation: true,
centeredScaling: true,
});
canvas.on("object:scaling", function() {
var obj = canvas.getActiveObject(),
width = obj.width,
height = obj.height,
scaleX = obj.scaleX,
scaleY = obj.scaleY;
obj.set({
width: width * scaleX,
height: height * scaleY,
scaleX: 1,
scaleY: 1,
});
});
rect.setControlsVisibility({
mt: false,
mb: false,
ml: false,
mr: false,
bl: false,
br: false,
tl: false,
tr: false,
mtr: false,
});
canvas.add(rect);
canvas.centerObject(rect);
canvas.setActiveObject(rect);
canvas.item(0).lockMovementX = true;
canvas.item(0).lockMovementY = true;
const width = document.getElementById('width');
const height = document.getElementById('height');
const adjust = (e) => {
Object.assign(rect, {
width: parseFloat(width.value) || 300,
height: parseFloat(height.value) || 400,
});
canvas.renderAll();
};
width.addEventListener('input', adjust);
height.addEventListener('input', adjust);
canvas.renderAll();
label, input {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/451/fabric.min.js"></script>
<canvas id="c" width="250" height="120"></canvas>
<div id="user-input">
<label for="width">Width:</label>
<input type="number" id="width" name="width" value="200" />
<label for="height">Height:</label>
<input type="number" id="height" name="height" value="80" />
</div>

Replacing functions without clearing the whole canvas?

I'm trying to replace text with functions, without clearing all drawn text. Right now I can replace a function but only by clearing the whole canvas. I'd like it to be a little bit more dynamic so that a third function (for example) would remain.
Here's what I've got so far; note how the original text is cleared:
var $ = function(id) {
return document.getElementById(id)
};
var canvas = this.__canvas = new fabric.Canvas('c');
canvas.setHeight(300);
canvas.setWidth(500);
function textOne() {
canvas.clear();
canvas.add(new fabric.IText('One', {
left: 50,
top: 100,
fontFamily: 'arial',
fill: '#333',
fontSize: 50
}));
}
// Text that should stay
canvas.add(new fabric.IText('This Should Stay The Same\nEdited Or Not', {
left: 300,
top: 45,
fontFamily: 'Monsieur La Doulaise',
fontSize: 27,
hasBorders: false,
hasControls: false,
selectable: true,
lockRotation: true,
lockMovementX: true,
lockMovementY: true,
align: 'mid',
originX: 'center',
originY: 'center',
centeredScaling: true,
}));
function textTwo() {
canvas.clear();
canvas.add(new fabric.IText('Two', {
left: 200,
top: 100,
fontFamily: 'arial black',
fill: '#333',
fontSize: 50
}));
}
canvas {
border: 1px solid #dddddd;
border-radius: 3px;
margin-top: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.min.js"></script>
<button onclick="textOne()">One</button>
<button onclick="textTwo()">Two</button>
<canvas id="c"></canvas>
Thanks in advance!
You just have to add an empty text inside your canvas and update it inside the corresponding functions. then execute canvas.renderAll after the updates. FYI, I have ZERO experience with fabric.js.
var $ = function(id) {
return document.getElementById(id)
};
var canvas = this.__canvas = new fabric.Canvas('c');
canvas.setHeight(300);
canvas.setWidth(500);
var dynamicText = new fabric.IText('', {
left: 50,
top: 100,
fontFamily: 'arial',
fill: '#333',
fontSize: 50
})
canvas.add(dynamicText);
function textOne() {
dynamicText.setText('ONE');
canvas.renderAll();
}
// Text that should stay
canvas.add(new fabric.IText('This Should Stay The Same\nEdited Or Not', {
left: 300,
top: 45,
fontFamily: 'Monsieur La Doulaise',
fontSize: 27,
hasBorders: false,
hasControls: false,
selectable: true,
lockRotation: true,
lockMovementX: true,
lockMovementY: true,
align: 'mid',
originX: 'center',
originY: 'center',
centeredScaling: true,
}));
function textTwo() {
dynamicText.setText('TWO');
canvas.renderAll();
}
canvas {
border: 1px solid #dddddd;
border-radius: 3px;
margin-top: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.min.js"></script>
<button onclick="textOne()">One</button>
<button onclick="textTwo()">Two</button>
<canvas id="c"></canvas>

Draw a rectangle preserving the image

I'm new to angular, html, javascript and I didn't figure out how to create a fiddle to explain my problem (sorry), so I uploaded to my dropbox: https://www.dropbox.com/s/60efg5vo3qql29c/image_canvas.zip?dl=0
So what I'm trying to do is upload an image from my database to a canvas and then draw one to many rectangles using fabricjs with a text in it, but when I do it, the image disappears and only appears the new drawn rectangle, what I'm doing wrong?
thanks in advance.
[EDIT]
This is my angular file
$scope.addRectangleToCanvas = function () {
try {
var c = document.getElementById('canvas');
var colorPicker = $('#btnColor');
var fontPicker = $('#bntfontsize');
var canvas = new fabric.Canvas('canvas', { selection: true });
var selText = $('#txtCustomComments').val();
var color = new fabric.Color(colorPicker.val()).toRgb();
var fontSize = fontPicker.val();
commentText = new fabric.IText(selText, {
fontSize: fontSize,
hasControls: false,
hasBorders: true,
originX: 'center',
backgroundColor: 'rgba(0,0,0,0)',
borderColor: color,
opacity: 0.6,
fill: color,
fontFamily: "helvetica"
});
var commentRectange = new fabric.Rect({
originX: 'center',
top: 30,
width: 300,
height: 200,
fill: 'rgba(0,0,0,0)',
stroke: color,
strokeWidth: 4
});
var group = new fabric.Group([commentText, commentRectange], {
left: 100,
top: 150
});
canvas.add(group);
canvas.bringForward(group);
} catch (exception) {
alert(exception);
console.log(exception);
}
};
$scope.GetNextNodes = function () {
var commentRectange, commentText;
var canvas = new fabric.Canvas('canvas', {
selection: true
});
var colorPicker = $('#btnColor');
var fontPicker = $('#bntfontsize');
var image = new Image();
var imageComp;
image.crossOrigin = "Anonymous";
image.onload = function () {
//imageComp = fabric.Image('https://upload.wikimedia.org/wikipedia/commons/0/0e/American_Black_Bear_%283405475634%29.jpg', function (img) {
imageComp = fabric.Image.fromURL('https://upload.wikimedia.org/wikipedia/commons/0/0e/American_Black_Bear_%283405475634%29.jpg', function (img) {
var oImg = img.set({
angle: 0,
cornersize: 10,
hoverCursor: "default",
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
hasRotatingPoint: true,
hasControls: false,
hasBorders: false,
lockScalingFlip: true,
lockScalingX: true,
lockScalingY: true,
lockSkewingX: true,
lockSkewingY: true,
height: image.naturalHeight,
width: image.naturalWidth
});
canvas.add(oImg).setActiveObject(oImg);
canvas.moveTo(oImg, window.objectIndex);
}, { crossOrigin: 'Anonymous' });
canvas.setHeight(image.naturalHeight);
canvas.setWidth(image.naturalWidth);
};
image.src = "https://upload.wikimedia.org/wikipedia/commons/0/0e/American_Black_Bear_%283405475634%29.jpg";
};
$scope.GetNextNodes();
This is my Html
<div id="page-wrapper">
<div class="main-dropdown">
<span>Nombre:</span>
<input id="txtCustomComments" name="Comment" value="zona" placeholder="Custom comments here" class="pls-select-comments" type="text">
<button name="btn-apply" id="btnApplyComments" value="apply" class="comment-apply-btn" ng-click="addRectangleToCanvas()">
Agregar
</button>
</div>
<div class="font-setting">
<div class="row">
<div class="color-picker">
<span>Font Color:</span>
<input id="btnColor" class="wmc-font-color" name="favcolor" value="#FF0000" type="color">
</div>
<div class="main-font-size">
<span>Font Size:</span>
<input id="bntfontsize" class="wmc-font-size" min="6" max="72" step="2" value="20" type="number">
</div>
</div>
</div>
<br />
<br/>
<canvas id="canvas" style="border:1px solid #d3d3d3;" />
</div>
You seem to be redeclaring the canvas in addRectangleToCanvas function with the
var canvas = new fabric.Canvas('canvas', { selection: true });
line. Try retaining the reference the getNextNodes function and then acting upon that
var canvas;
$scope.GetNextNodes = function () {
canvas = new fabric.Canvas('canvas');
//...
}
$scope.addRectangleToCanvas = function () {
canvas.add... // Do whatnot here with the canvas w/o redeclaring
}
Declaring the variables under $scope helped.
$scope.commentRectange;
$scope.commentText;
$scope.canvas = new fabric.Canvas('wmcCanvas', {
selection: true
});
$scope.colorPicker = $('#btnColor');
$scope.fontPicker = $('#bntfontsize');
$scope.image = new Image();
$scope.imageComp;
$scope.oImg;
$scope.group;

Modifying Grid Height/Width

I am working on a project that is a world planner/builder for a game that I enjoy. I want to make the grid 100 * 54. I have been messing around with some code on JSFiddle and I can't seem to get what I want. I would like the size of the grid squares to be 16*16
var canvas = new fabric.Canvas('c', {
selection: false
});
var grid = 50;
// create grid
for (var i = 0; i < (600 / grid); i++) {
canvas.add(new fabric.Line([i * grid, 0, i * grid, 600], {
stroke: '#ccc',
selectable: false
}));
canvas.add(new fabric.Line([0, i * grid, 600, i * grid], {
stroke: '#ccc',
selectable: false
}))
}
// add objects
canvas.add(new fabric.Rect({
left: 100,
top: 100,
width: 50,
height: 50,
fill: '#faa',
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
originX: 'left',
originY: 'top',
centeredRotation: true,
hasControls: false
}));
// snap to grid
canvas.on('object:moving', function(options) {
options.target.set({
left: Math.round(options.target.left / grid) * grid,
top: Math.round(options.target.top / grid) * grid
});
});
canvas {
border: 1px solid #ccc;
}
<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" width="600" height="600"></canvas>
The way this works:
Grid is how big your grid will be in pixels.
You need two for loops to indicate how many lines for each.
The for loops generate the grid based on how many loops it goes through. (so 54 means 54 lines.)
The 2000 indicates how long the line will be in pixels.
Working code:
var canvas = new fabric.Canvas('c', {
selection: false
});
var grid = 16;
// create grid
for (var i = 0; i < 100; i++) {
canvas.add(new fabric.Line([i * grid, 0, i * grid, 2000], {
stroke: '#ccc',
selectable: false
})); // horizontal
}
for (var i = 0; i < 54; i++) {
canvas.add(new fabric.Line([0, i * grid, 2000, i * grid], {
stroke: '#ccc',
selectable: false
})); // vertical
}
// add objects
canvas.add(new fabric.Rect({
left: 16,
top: 16,
width: 16,
height: 16,
fill: '#faa',
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
originX: 'left',
originY: 'top',
centeredRotation: true,
hasControls: false
}));
// add objects
canvas.add(new fabric.Rect({
left: 16,
top: 16,
width: 16,
height: 16,
fill: '#faa',
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
originX: 'left',
originY: 'top',
centeredRotation: true,
hasControls: false
}));
// snap to grid
canvas.on('object:moving', function(options) {
options.target.set({
left: Math.round(options.target.left / grid) * grid,
top: Math.round(options.target.top / grid) * grid
});
});
canvas {
border: 1px solid #ccc;
}
<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" width="1600px" height="865px" ></canvas>

Fabric.js : How to set a custom size to Text or IText?

I am using the excellent Fabric.js to draw some text in a canvas. When I specify a custom size ( let's say a 200x200 rectangle) to my IText Object, it seems that Farbric.js force the width and the height of the object to fit around the text.
var t = new fabric.IText("Hello world !", {
top: 100,
left: 100,
width: 200,
height:200,
backgroundColor: '#FFFFFF',
fill: '#000000',
fontSize: 12,
lockScalingX: true,
lockScalingY: true,
hasRotatingPoint: false,
transparentCorners: false,
cornerSize: 7
});
Here is a Fiddle with my problem : http://jsfiddle.net/K52jG/4/
In this example, I want the "Hello World!" text to be in a 200x200 box. Is it possible to do that, and how ?
OK, so because it is not possible, the best way I found to is to nest the IText box in a Rectangle object, using a Group
var r = new fabric.Rect({
width: 200,
height: 200,
fill: '#FFFFFF',
});
// create a rectangle object
var t = new fabric.IText("Hello world !", {
backgroundColor: '#FFFFFF',
fill: '#000000',
fontSize: 12,
top : 3,
});
var group = new fabric.Group([ r, t ], {
left: 100,
top: 100,
lockScalingX: true,
lockScalingY: true,
hasRotatingPoint: false,
transparentCorners: false,
cornerSize: 7
});
Example in this Fiddle : http://jsfiddle.net/4HE3U/1/
Note : I have to set a "top" value to the text because it goes out of the box. The value seem to be : fontSize / 4

Categories