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>
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 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>
I'm trying to create several groups. Each of the group will get a custom attribute to indicate its position. Depending on this attribute I want to set different images as a group members. This part doesn't work and I cannot figure it out. image header.png is added twice and shows in content-footer area. in the header area there is no image at all.
Also when I try to log the output
console.log(JSON.stringify(canvasBuild)
there is no content inside the "objects" property of a group. When I try to log stringified group - it does show the image objects.
This is the simplified code so far
var hasHeader = false;
var hasFooter = false;
// users choice of layout
var customPosition = [];
$(this).siblings('.layoutOptions').children('input:checked').each(function(i)
{
customPosition[i] = $(this).val();
if ( customPosition[i]=='content-header'){
hasHeader = true;
}else if (customPosition[i]=='content-footer'){
hasFooter = true;
}
});
$(this).siblings('.contentOptions').children('input:checked').val();
for (i = 0; i< customPosition.length; i++){
console.log(i);
console.log(customPosition[i]);
var canvField = (canvasBHeight - shadowBVal*2) - strokeBWidth*2;
if (customPosition[i] == 'content-header'){
//header positioning
var group = new fabric.Group([],{
left: strokeBWidth + 10,
stroke: '#ddd',
strokeLineJoin: 'round',
strokeWidth: 2,
hasControls: true,
lockMovementX: false,
lockMovementY: false,
lockRotation: true,
selectable: true,
position: posB,
fill: '#ccc',
width: ((canvasBWidth - shadowBVal*2) - strokeBWidth*2 - 10),
//height: canvasBHeight/5 - strokeBWidth,
height: canvField/5,
top: strokeBWidth,
ry: 0,
rx: 20,
//originX: 'center',
//originY: 'center'
});
//add dummy content
fabric.Image.fromURL('image/bordenconfigurator/dummies/header.png', function(oImg) {
oImg.set({
});
group.add(oImg);
console.log('group '+i+' '+customPosition[i]+':');
console.log(JSON.stringify(group));
//console.log(group);
});
}else if(customPosition[i] == 'content-footer') {
console.log(customPosition[i]);
//footer positioning
var group = new fabric.Group([],{
left: strokeBWidth + 10,
stroke: '#ddd',
strokeLineJoin: 'round',
strokeWidth: 2,
hasControls: true,
lockMovementX: false,
lockMovementY: false,
lockRotation: true,
selectable: true,
position: posB,
fill: '#ccc',
width: ((canvasBWidth - shadowBVal*2) - strokeBWidth*2 - 10),
//height: canvasBHeight/5 - strokeBWidth,
height: canvField/5,
top: strokeBWidth + canvField*4/5 + 10,
ry: 0,
rx: 20,
//originX: 'center',
//originY: 'center'
});
//add dummy content
fabric.Image.fromURL('image/bordenconfigurator/dummies/footer.png', function(oImg) {
oImg.set({
top: canvasBHeight- strokeBWidth-shadowBVal,
});
group.add(oImg);
canvasBuild.centerObjectH(oImg);
console.log('group '+i+' '+customPosition[i]+':');
console.log(JSON.stringify(group));
});
}else{
console.log(customPosition[i]);
//body positioning
var height,top;
if (hasHeader === true && hasFooter === true){
height = canvField*3/5 + 5;
top = strokeBWidth + canvField/5 + 5;
}else if(hasHeader ===true && hasFooter === false){
height = canvField*4/5 + 5;
top = strokeBWidth + canvField/5 + 5;
}else if(hasHeader ===false && hasFooter === true){
height = canvField*4/5;
top = strokeBWidth + 5;
}
var group = new fabric.Group([],{
left: strokeBWidth + 10,
stroke: '#ddd',
strokeLineJoin: 'round',
strokeWidth: 2,
hasControls: true,
lockMovementX: false,
lockMovementY: false,
lockRotation: true,
selectable: true,
position: posB,
fill: 'rgba(255,255,255,0)',
width: ((canvasBWidth - shadowBVal*2) - strokeBWidth*2 - 10),
height: height,
//top: strokeBWidth + canvField/5 + 5,
top: top,
ry: 20,
rx: 20
});
}
group.toObject = (function(toObject) {
return function() {
return fabric.util.object.extend(toObject.call(this), {
customPosition: this.customPosition,
label: this.label,
id: this.id
});
};
})(group.toObject);
group.customPosition = customPosition[i];
group.id = posB;
canvasBuild.add(group);
group.length = 0;
posB++;
}
//addOrientationLines();
//canvasBuild.renderAll();
//console.log(JSON.stringify(canvasBuild));
});
I do not want uniform scaling at corner points so i have added uniScaleTransform property to true at the time of initiation but it is not working , still it's scaling uniformly.I am using fabric js version 1.6.3, below is the code,
<!-- fabric js code-->
var canvas = new fabric.Canvas('canvas', {
uniScaleTransform : true
});
I've added a working example below that seem to work fine. If you are seeing different results please do post a snippet of working code demonstrating the problem.
var canvas = new fabric.Canvas('c', { uniScaleTransform : true, preserveObjectStacking:true });
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
}));
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.3/fabric.min.js"></script>
<canvas id="c" width="600" height="600"></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,
});
}