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>
Related
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>
I'm working on a FabricJS app where I want the user to move objects to the canvas origin by a simple click, even after rotating or scaling objects or a group of objects.
To do so, when clicking I perform the code below when the user click:
obj.left= obj.getBoundingRect().width/2;
obj.top=obj.getBoundingRect().height/2;
It works perfectly with rectangles or texts, but it doesn't work at all when using shape, svg or even group of objects. I have no idea why!
Below it's the complete code.
IMPORTANT: Please enable cross-origin access in your browser for the svg to be loaded.
this.__canvas = new fabric.Canvas('meCanvas', {
preserveObjectStacking: true,
height: 400,
width: 400,
backgroundColor: '#1F1F1F',
canvasKey:'azpoazpoaz'
});
let rect = new fabric.Rect({
fill: 'red',
width: 200,
height: 100,
left: 100,
top: 100,
originX: 'center',
originY: 'center',
fontWeight: 'normal'
});
let text = new fabric.IText('Text', {
fontFamily: 'Times',
fontSize: 18,
fill: 'white',
left: 100,
top: 100,
originX: 'center',
originY: 'center',
fontWeight: 'normal'
});
let url = 'https://svgshare.com/i/9DX.svg';
fabric.loadSVGFromURL(url,(objects,options)=> {
let loadedObjects = fabric.util.groupSVGElements(objects, options);
loadedObjects.left=50;
loadedObjects.top=150;
this.__canvas.add(loadedObjects);
});
let url2 = 'https://svgshare.com/i/9Cp.svg';
fabric.loadSVGFromURL(url2,(objects,options)=> {
let loadedObjects = fabric.util.groupSVGElements(objects, options);
loadedObjects.left=100;
loadedObjects.top=200;
loadedObjects.angle=20;
this.__canvas.add(loadedObjects);
});
this.__canvas.add(rect);
this.__canvas.add(text);
this.__canvas.renderAll();
$('#pushorigin').click((e)=>{
let obj = this.__canvas.getActiveObject();
obj.left= obj.getBoundingRect().width/2;
obj.top=obj.getBoundingRect().height/2;
this.__canvas.requestRenderAll();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.3/fabric.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<div style='display: inline-block'>
<div>
<canvas id='meCanvas' ref='meFabric'/>
</div>
<div>
<button id='pushorigin'>Push to origin</button>
</div>
</div>
If you select the text or the rectangle and click the button "Push to origin", they will go to the origin (top left corner). But the same action doesn't work with the svg (triangle and the polygone) and it goes even worst if we stretch those svg or even rotating them, they won't go to the origin. Same thing when using a group of object!
I don't know what to do, I'm lost. I've googled for days and found nothing.
Please help!
Set originX: 'center',originY: 'center' for all object then your solution will work.
this.__canvas = new fabric.Canvas('meCanvas', {
preserveObjectStacking: true,
height: 400,
width: 400,
backgroundColor: '#1F1F1F',
canvasKey: 'azpoazpoaz'
});
let rect = new fabric.Rect({
fill: 'red',
width: 200,
height: 100,
left: 100,
top: 100,
originX: 'center',
originY: 'center',
fontWeight: 'normal'
});
let text = new fabric.IText('Text', {
fontFamily: 'Times',
fontSize: 18,
fill: 'white',
left: 100,
top: 100,
originX: 'center',
originY: 'center',
fontWeight: 'normal'
});
let url = 'https://svgshare.com/i/9DX.svg';
fabric.loadSVGFromURL(url, (objects, options) => {
let loadedObjects = fabric.util.groupSVGElements(objects, options);
loadedObjects.set({
left: 50,
top: 150,
originX: 'center',
originY: 'center'
})
this.__canvas.add(loadedObjects);
});
let url2 = 'https://svgshare.com/i/9Cp.svg';
fabric.loadSVGFromURL(url2, (objects, options) => {
let loadedObjects = fabric.util.groupSVGElements(objects, options);
loadedObjects.set({
left: 100,
top: 200,
angle: 20,
originX: 'center',
originY: 'center'
})
this.__canvas.add(loadedObjects);
});
this.__canvas.add(rect);
this.__canvas.add(text);
this.__canvas.renderAll();
$('#pushorigin').click((e) => {
let obj = this.__canvas.getActiveObject();
obj.set({
left: obj.getBoundingRect().width / 2,
top: obj.getBoundingRect().height / 2
}).setCoords()
this.__canvas.requestRenderAll();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.3/fabric.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<div style='display: inline-block'>
<div>
<canvas id='meCanvas' ref='meFabric'/>
</div>
<div>
<button id='pushorigin'>Push to origin</button>
</div>
</div>
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>
Can you please help me in creating a context menu on the fabric object. I have googled a lot but couldn't find exact solution. I have created two objects as below on the fabric. How can i bind a context menu to the fabric object?
Fiddle link: http://jsfiddle.net/fabricjs/S9sLu/
canvas.add(new fabric.Rect({
left: 100,
top: 100,
width: 50,
height: 50,
fill: '#faa',
originX: 'left',
originY: 'top',
centeredRotation: true
}));
canvas.add(new fabric.Circle({
left: 300,
top: 300,
radius: 50,
fill: '#9f9',
originX: 'left',
originY: 'top',
centeredRotation: true
}));
Thanks in advance.
I'm using contextMenu.js, this should get you started:
function contextMenu () {
var ctxTarget = null;
var menu = [{
name: 'Select Object',
img: '',
title: 'Select Object',
fun: function (o, jqEvent) {
canvas.setActiveObject(ctxTarget);
console.log(ctxTarget);
}
}];
$('.upper-canvas').on('contextmenu', function (e) {
e.preventDefault();
ctxTarget = canvas.findTarget(e.originalEvent);
});
$('.upper-canvas').contextMenu(menu, {
triggerOn: 'contextmenu',
closeOnClick: true,
});
}
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>