So I'm currently working on a project that requires similar functionality to what you would expect in a floorplan designing application, and I need to be able to essentially draw a polygon (room) by connecting points generated on-click, with lines (walls).
I've found a few really great examples that resemble the functionality I'm looking for, but I'm unsure of how to approach implementing it as a functional react component.
I've seen Fabric.js and Konva.js used in a few other implementations of this kind of functionality, but I can't seem to be able to find a react example to use as a reference..
Below is an example I found on codepen using jQuery
var min = 99;
var max = 999999;
var polygonMode = true;
var pointArray = new Array();
var lineArray = new Array();
var activeLine;
var activeShape = false;
var canvas
$(window).load(function() {
prototypefabric.initCanvas();
$('#create-polygon').click(function() {
prototypefabric.polygon.drawPolygon();
});
});
var prototypefabric = new function() {
this.initCanvas = function() {
canvas = window._canvas = new fabric.Canvas('c');
canvas.setWidth($(window).width());
canvas.setHeight($(window).height() - $('#nav-bar').height());
//canvas.selection = false;
canvas.on('mouse:down', function(options) {
if (options.target && options.target.id == pointArray[0].id) {
prototypefabric.polygon.generatePolygon(pointArray);
}
if (polygonMode) {
prototypefabric.polygon.addPoint(options);
}
});
canvas.on('mouse:up', function(options) {
});
canvas.on('mouse:move', function(options) {
if (activeLine && activeLine.class == "line") {
var pointer = canvas.getPointer(options.e);
activeLine.set({
x2: pointer.x,
y2: pointer.y
});
var points = activeShape.get("points");
points[pointArray.length] = {
x: pointer.x,
y: pointer.y
}
activeShape.set({
points: points
});
canvas.renderAll();
}
canvas.renderAll();
});
};
};
prototypefabric.polygon = {
drawPolygon: function() {
polygonMode = true;
pointArray = new Array();
lineArray = new Array();
activeLine;
},
addPoint: function(options) {
var random = Math.floor(Math.random() * (max - min + 1)) + min;
var id = new Date().getTime() + random;
var circle = new fabric.Circle({
radius: 5,
fill: '#ffffff',
stroke: '#333333',
strokeWidth: 0.5,
left: (options.e.layerX / canvas.getZoom()),
top: (options.e.layerY / canvas.getZoom()),
selectable: false,
hasBorders: false,
hasControls: false,
originX: 'center',
originY: 'center',
id: id,
objectCaching: false
});
if (pointArray.length == 0) {
circle.set({
fill: 'red'
})
}
var points = [(options.e.layerX / canvas.getZoom()), (options.e.layerY / canvas.getZoom()), (options.e.layerX / canvas.getZoom()), (options.e.layerY / canvas.getZoom())];
line = new fabric.Line(points, {
strokeWidth: 2,
fill: '#999999',
stroke: '#999999',
class: 'line',
originX: 'center',
originY: 'center',
selectable: false,
hasBorders: false,
hasControls: false,
evented: false,
objectCaching: false
});
if (activeShape) {
var pos = canvas.getPointer(options.e);
var points = activeShape.get("points");
points.push({
x: pos.x,
y: pos.y
});
var polygon = new fabric.Polygon(points, {
stroke: '#333333',
strokeWidth: 1,
fill: '#cccccc',
opacity: 0.3,
selectable: false,
hasBorders: false,
hasControls: false,
evented: false,
objectCaching: false
});
canvas.remove(activeShape);
canvas.add(polygon);
activeShape = polygon;
canvas.renderAll();
} else {
var polyPoint = [{
x: (options.e.layerX / canvas.getZoom()),
y: (options.e.layerY / canvas.getZoom())
}];
var polygon = new fabric.Polygon(polyPoint, {
stroke: '#333333',
strokeWidth: 1,
fill: '#cccccc',
opacity: 0.3,
selectable: false,
hasBorders: false,
hasControls: false,
evented: false,
objectCaching: false
});
activeShape = polygon;
canvas.add(polygon);
}
activeLine = line;
pointArray.push(circle);
lineArray.push(line);
canvas.add(line);
canvas.add(circle);
canvas.selection = false;
},
generatePolygon: function(pointArray) {
var points = new Array();
$.each(pointArray, function(index, point) {
points.push({
x: point.left,
y: point.top
});
canvas.remove(point);
});
$.each(lineArray, function(index, line) {
canvas.remove(line);
});
canvas.remove(activeShape).remove(activeLine);
var polygon = new fabric.Polygon(points, {
stroke: '#333333',
strokeWidth: 0.5,
fill: 'red',
opacity: 1,
hasBorders: false,
hasControls: false
});
canvas.add(polygon);
activeLine = null;
activeShape = null;
polygonMode = false;
canvas.selection = true;
}
};
* {
font-family: "Roboto", sans-serif;
font-weight: 100;
}
body {
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.2.0/fabric.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css" rel="stylesheet" />
<nav class="grey darken-4" role="navigation" id="nav-bar" style="height:50px;">
<div class="nav-wrapper container">
<a id="logo-container" class="brand-logo" style="line-height: 50px;font-size: 20px;">Fabric Polygon</a>
<ul id="nav-mobile" class="right hide-on-med-and-down">
<li><a id="create-polygon" style="line-height: 50px;font-size: 20px;cursor:pointer;">Create Polygon</a></li>
</ul>
</div>
</nav>
<div class="section no-pad-bot no-pad-top">
<canvas id="c"></canvas>
</div>
And this is essentially the end functionality I'd like to achieve.
I really appreciate any help or advice!
To begin with, you should keep in mind that react is meant for structuring the visual components of your application and their interaction with the rest of your code, that is to say react is not a framework that covers everything you may need.
I'm not even sure that react is what you need, maybe you need a canvas. Maybe the canvas resides in a react component.
Nevertheless, if you decide that you would like to go at it with react then lets break down what you need, visually:
a "canvas" component which contains all your polygons or lines
a "polygon" which contains lines
a "line" component.
to begin with, before the interaction, make sure you create a state that makes sense that will be sent to the canvas, maybe an array of polygon or line objects. a line object is clearly two points, maybe a color, etc.
If you got this far with a mock of your polgyon state, its time to add a state manager (like redux) and some event listeners to update the state when events happen in the DOM (like mouse or keyboard events.)
To summarize:
Use react for visual elements.
Use redux to manage the state of the canvas.
When initializing the parent component (the "canvas"), create event listeners that fire actions that affect the state.
Related
I have a function that hides and shows an image whenever it's clicked, but I want to do the same thing, by clicking another object.
Here is the code for the image
function drawCheckbox(left, top, width, height){
var imgClass = new fabric.Image.fromURL('https://image.flaticon.com/icons/svg/33/33281.svg',function(img){
img.width = width;
img.height = height;
img.left = left;
img.top = top;
img.set({
hoverCursor: 'default',
selectable: true,
opacity: 0
})
img.on('mousedown', function(e) {
if(e.target.opacity <= 0.5){
e.target.opacity = 1;
}else{
e.target.opacity = 0;
}
canvas.renderAll();
});
canvas.add(img);
canvas.renderAll();
})
}
And this is the code for the rectangle object:
function addRect(left, top, width, height, id) {
const o = new fabric.Rect({
width: width,
height: height,
fill: tableFill,
stroke: tableStroke,
strokeWidth: 2,
shadow: tableShadow,
originX: 'center',
originY: 'center',
centeredRotation: true,
snapAngle: 45,
selectable: true
})
const t = new fabric.IText(number.toString(), {
fontFamily: 'Calibri',
fontSize: 14,
fill: '#fff',
textAlign: 'center',
originX: 'center',
originY: 'center'
})
const g = new fabric.Group([o, t], {
left: left,
top: top,
centeredRotation: true,
snapAngle: 45,
selectable: true,
type: 'table',
id: id,
number: number
})
canvas.add(g)
number++
g.on('selected', function () {
// here I want to make de image dissapear, when the object is clicked
})
canvas.hoverCursor = 'pointer';
canvas.discardActiveObject();
canvas.renderAll();
return g;
}
I tried creating the image inside the rectangle, but it doesn't do anything when the rectangle it's clicked. Does anyone had a similar problem?
Here is the codepen: codepen.io/Zerro1/pen/PoZvmOE .
Here i have created a codepen for you, this is one way to do it. I tried to create a square and clicking on that square i am toggling the last checkbox visibility.
portion of code:
var square = new fabric.Rect({
width: 100,
height: 100,
fill: '#000',
left:120
});
square.on('mousedown', function(e) {
if(img.opacity <= 0.5){
img.opacity = 1;
}else{
img.opacity = 0;
}
canvas.renderAll();
});
canvas.add(img);
canvas.add(square);
canvas.renderAll();
I am coding a web page using Fabricjs. First of all, I draw some circles in my canvas, then I allow the user to select one or more of them. I wrote a selection:created handler that creates a form and prints the coordinates of each selected object in that form.
The problem is that those coordinates are completely different from the ones I set when I created the circles. However, this happens only when the user select two or more circle.
What I basically do is: I call the getActiveObjects() method that returns an activeObjects[] array, then I loop to access the left and top properties of each element of that array. I have also tried to make same manipulation of those numbers as this answer suggests: How to set relative position (oCoords) in FabricJs?
This is how I create circles:
function drawCircle( x, y, r, color ){
var circle = new fabric.Circle({radius: r, fill: color, originX: 'center', originY: 'center', left: x, top: y, lockMovementX: true, lockMovementY: true, hasControls: false});
canvas.add(circle);
}
And this is the selection:created handler:
canvas.on('selection:created', function(e){
var activeObjects = canvas.getActiveObjects();
var length = activeObjects.length;
var myForm = document.createElement("form");
for( var i=0; i<length; i++){
var x = document.createElement("input");
x.setAttribute('type',"text");
x.setAttribute('name', "x"+i);
x.setAttribute('value', activeObjects[i].left);
var y = document.createElement("input");
y.setAttribute('type',"text");
y.setAttribute('name', "y"+i);
y.setAttribute('value', activeObjects[i].top );
var radius = document.createElement("input");
radius.setAttribute('type',"text");
radius.setAttribute('name', "radius"+i);
radius.setAttribute('value', activeObjects[i].radius );
f.appendChild(radius);
f.appendChild(x);
f.appendChild(y);
}
})
I expect that if I create two circles calling drawCircle(10,20,2,'black') and drawCircle(30,30,2,'black') and I select them, I will read in the form the values 10, 20, 2, 30, 30 and 2; instead, only the radius values are correct, but the coordinates are respectively (-10, 395) and (10, 405). As you can see, the distance between the two circle is still the same, but their coordinates are completely different from the original.
Moreover, if I modify the selection (let's say I select 3 circles), in the form I will read other completely different values.
var canvas = new fabric.Canvas('myCanvas');
fabric.Group.prototype.lockMovementX = true;
fabric.Group.prototype.lockMovementY = true;
fabric.Group.prototype.hasControls = false;
var circle = new fabric.Circle({radius: 2, fill: 'black', originX: 'center', originY: 'center', left: 10, top: 400-20, lockMovementX: true, lockMovementY: true, hasControls: false});
canvas.add(circle);
var circle = new fabric.Circle({radius: 2, fill: 'black', originX: 'center', originY: 'center', left: 30, top: 400-30, lockMovementX: true, lockMovementY: true, hasControls: false});
canvas.add(circle);
canvas.on('selection:created', function(e){
var activeObjects = canvas.getActiveObjects();
var length = activeObjects.length;
var activeSelection = canvas.getActiveObject();
var matrix = activeSelection.calcTransformMatrix();
var form = document.createElement("form");
for( var i=0; i<length; i++){
var obj = activeObjects[i];
var objectPosition = { x: obj.left, y: obj.top };
var finalPosition = fabric.util.transformPoint(objectPosition, matrix);
var radius = document.createElement("input");
radius.setAttribute('type',"text");
radius.setAttribute('name', "raggio"+i);
radius.setAttribute('value', obj.radius );
var x = document.createElement("input");
x.setAttribute('type',"text");
x.setAttribute('name', "x"+i);
x.setAttribute('value', finalPosition.x );
var y = document.createElement("input");
y.setAttribute('type',"text");
y.setAttribute('name', "y"+i);
y.setAttribute('value', 400-finalPosition.y );
form.appendChild(radius);
form.appendChild(x);
form.appendChild(y);
}
document.getElementsByTagName('body')[0].appendChild(form);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.6/fabric.min.js"></script>
<h1> Grid </h1>
<canvas id="myCanvas" width="600" height="400" style="border: 1px solid #000000;"></canvas>
you have to take in consideration the transform of the object that is applied by the group.
When you just make the selection you may think there are no transformation, but fabricjs is drawing those circles around the center of the selection, there is a translate operation.
A way to solve it generally for any transform ( scale, rotate, ) is:
var canvas = new fabric.Canvas('myCanvas');
fabric.Group.prototype.lockMovementX = true;
fabric.Group.prototype.lockMovementY = true;
fabric.Group.prototype.hasControls = false;
var circle = new fabric.Circle({radius: 2, fill: 'black', originX: 'center', originY: 'center', left: 10, top: 20, lockMovementX: true, lockMovementY: true, hasControls: false});
canvas.add(circle);
var circle = new fabric.Circle({radius: 2, fill: 'black', originX: 'center', originY: 'center', left: 30, top: 30, lockMovementX: true, lockMovementY: true, hasControls: false});
canvas.add(circle);
canvas.on('selection:created', function(e){
var activeObjects = canvas.getActiveObjects();
var length = activeObjects.length;
var activeSelection = canvas.getActiveObject();
var matrix = activeSelection.calcTransformMatrix();
var form = document.createElement("form");
for( var i=0; i<length; i++){
var obj = activeObjects[i];
var objectPosition = { x: obj.left, y: obj.top };
var finalPosition = fabric.util.transformPoint(objectPosition, matrix);
var radius = document.createElement("input");
radius.setAttribute('type',"text");
radius.setAttribute('name', "raggio"+i);
radius.setAttribute('value', obj.radius );
var x = document.createElement("input");
x.setAttribute('type',"text");
x.setAttribute('name', "x"+i);
x.setAttribute('value', finalPosition.x );
var y = document.createElement("input");
y.setAttribute('type',"text");
y.setAttribute('name', "y"+i);
y.setAttribute('value', finalPosition.y );
form.appendChild(radius);
form.appendChild(x);
form.appendChild(y);
}
document.getElementsByTagName('body')[0].appendChild(form);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.6/fabric.min.js"></script>
<h1> Grid </h1>
<canvas id="myCanvas" width="100" height="100" style="border: 1px solid #000000;"></canvas>
Loos pretty solid to me now that i updated my answer with your code.
See Fiddle: I am trying to render a background image on a FabricJS polygon, but for some reason the scale of the pattern is different on different devices / browsers for no obvious reason.
Here is the code:
HTML:
<canvas id="tcanvas" width="200" height="200" />
JavaScript:
function testPattern(id) {
var url = 'https://dummyimage.com/200x200/aff/000',
tCanvas = new fabric.Canvas(id);
new fabric.Image.fromURL(url, function(img) {
var tPoints = [{
x: 0,
y: 0
},
{
x: 0,
y: 200
},
{
x: 200,
y: 200
},
{
x: 200,
y: 0
}
];
var tPatternWidth = 200;
var tPatternHeight = 200;
var tImgXScale = tPatternWidth / img.width;
var tImgYScale = tPatternHeight / img.height;
img.set({
left: 0,
top: 0,
scaleX: tImgXScale,
scaleY: tImgYScale
});
var tPatternSourceCanvas = new fabric.StaticCanvas();
tPatternSourceCanvas.add(img);
tPatternSourceCanvas.renderAll();
var tPattern = new fabric.Pattern({
offsetX: 0,
offsetY: 0,
source: function() {
tPatternSourceCanvas.setDimensions({
width: tPatternWidth,
height: tPatternHeight
});
tPatternSourceCanvas.renderAll();
return tPatternSourceCanvas.getElement();
},
repeat: 'no-repeat'
});
var tImgPoly = new fabric.Polygon(tPoints, {
left: 100,
top: 100,
originX: 'center',
originY: 'center',
fill: tPattern,
objectCaching: false,
selectable: false,
stroke: 'red',
strokeWidth: 1
});
tCanvas.add(tImgPoly);
tCanvas.renderAll();
});
}
testPattern('tcanvas');
This is what I see on my desktop Chrome:
... and this what I see on a (Samsung) tablet Chrome:
How can I fix the code so that the both patterns look the same, i.e., like the first one?
It s a retina support side effect.
When initializing the canvas for the Pattern, please pass as option:
enableRetinaScaling: false
var tPatternSourceCanvas = new fabric.StaticCanvas(null, {enableRetinaScaling: false});
This will avoid fabric trying to enhance the canvas twice.
var canvas = new fabric.Canvas('canvas');
canvas.setWidth(500);
canvas.setHeight(500);
canvas.setBackgroundColor('#ccc');
canvas.allowTouchScrolling = true;
var line = new fabric.Path('M100,350 Q200,100 300,350', {
fill: '',
stroke: 'red',
strokeWidth: 5,
objectCaching: false
});
var circle = new fabric.Circle({
radius: 15,
fill: 'blue',
lockScalingY: true,
lockScalingX: true,
left: 300,
top: 350
});
canvas.add(line, circle);
canvas.on('object:moving', function(event) {
var path = canvas.item(0);
path.path[1][3] = event.target.left;
path.path[1][4] = event.target.top;
path.setCoords();
canvas.calcOffset();
canvas.renderAll();
});
<script src="//cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.js"></script>
<canvas id="canvas"></canvas>
show demo on fiddle
When I move circle there is a change path of line. After change path of line I see wrong bounding rectangles.
How I can fix that?
I can just remove line and create again, but maybe there is an easier way to do it.
You can actually recalculate the bounding box, but you have to do by yourself.
So is actually tricky, you need to remove the current pathOffset from path, and then call the internal method to recalculate the bbox.
It works, but being an internal method and a non supported feature, it could be changed in the future.
var canvas = new fabric.Canvas('canvas');
canvas.setWidth(500);
canvas.setHeight(500);
canvas.setBackgroundColor('#ccc');
canvas.allowTouchScrolling = true;
var line = new fabric.Path('M100,350 Q200,100 300,350', {
fill: '',
stroke: 'red',
strokeWidth: 5,
objectCaching: false
});
var circle = new fabric.Circle({
radius: 15,
fill: 'blue',
lockScalingY: true,
lockScalingX: true,
left: 300,
top: 350
});
canvas.add(line, circle);
canvas.on('object:moving', function(event) {
var path = canvas.item(0);
path.path[1][3] = event.target.left;
path.path[1][4] = event.target.top;
path.pathOffset = null;
path._setPositionDimensions({});
path.setCoords();
canvas.calcOffset();
canvas.renderAll();
});
<script src="//cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.20/fabric.js"></script>
<canvas id="canvas"></canvas>
I am working at a project with fabric js. I tried to minimize my problem, so I'm hoping that the code isn't too messed up.
I am creating some Objects which are linked with each other:
A Line, which contains a Start and an Endpoint
A Circle, which is StartPoint of 1 line and Endpoint of another line
with this combination i can create different shapes(like a polygon) and modify my move-functions for them too.
When a Circle is dragged, the related Lines are scaling and moving too. (in my code you can move the lines too and the shape is resized after that, but i didnt put it into this example, bc this short extract should be enough to show what my problem is.)
I got a little example in jsfiddle: https://jsfiddle.net/bxgox7cr/
When you look at the ends of the lines, you can clearly see a cut, so the eye soon recognize, that this is not a connected shape but rather some lines which are close to each other. Is there a way to modify the look of the lines, that the shape looks "closed"?
Here is my code, i tried to put some comments, that it is easy to read:
var canvas = new fabric.Canvas('canvas');
fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';
document.getElementById("canvas").tabIndex = 1000;
/** ------------creating a Line Object, which contains a start and an endpoint ------------**/
fabric.LineWithPoints = fabric.util.createClass(fabric.Line, {
initialize: function(points, options) {
options || (options = {});
this.callSuper('initialize', points, options);
options &&
this.set('type', options.type),
this.set('name', options.name),
this.set('start_point', options.start_point),
this.set('end_point', options.end_point),
this.set('current_x', options.current_x),
this.set('current_y', options.current_y)
},
setStartPointAndEndPoint: function(start_point, end_point) {
this.set({
start_point: start_point,
end_point: end_point
});
},
setValues: function(new_x1, new_y1, new_x2, new_y2) {
// console.log(this);
this.set({
x1: new_x1,
x2: new_x2,
y1: new_y1,
y2: new_y2
});
this.setCoords();
}
});
/**--- modifie the circle element, adding new functions for the movement of the object-------*/
fabric.LinePoint = fabric.util.createClass(fabric.Circle, {
initialize: function(options) {
options || (options = {});
this.callSuper('initialize', options);
options &&
this.set('subtype', 'line_point'),
this.set('x', this.left),
this.set('y', this.top)
},
setPointCoordinates: function(new_left, new_top) {
this.set({
x: new_left,
y: new_top,
left: new_left,
top: new_top
});
this.setCoords();
},
move: function(new_left, new_top) {
var wall_1 = this.line1;
var wall_2 = this.line2;
this.setPointCoordinates(new_left, new_top);
wall_1.setValues(wall_1.x1, wall_1.y1, this.getLeft(), this.getTop());
wall_2.setValues(this.getLeft(), this.getTop(), wall_2.x2, wall_2.y2);
canvas.renderAll();
},
});
/**------------------- Moving Function------------------------------------------------- */
canvas.on('object:moving', function(event) {
var object = event.target;
if (object.subtype == "line_point") {
object.move(object.getLeft(), object.getTop());
}
});
/**------------------------------ create functions for the objects -----------------------*/
function newCircleObject(left, top, wall_1, wall_2) {
var circle = new fabric.LinePoint({
left: left,
top: top,
strokeWidth: 2,
radius: 15,
fill: 'grey',
stroke: 'black',
opacity: 0.1,
perPixelTargetFind: true,
subtype: 'line_point',
includeDefaultValues: false
});
circle.hasControls = false;
circle.hasBorders = false;
circle.line1 = wall_1;
circle.line2 = wall_2;
return circle;
}
function newWallObject(coords) {
var wall = new fabric.LineWithPoints(coords, {
stroke: 'black',
strokeWidth: 6,
lockScalingX: true,
lockScalingY: true,
perPixelTargetFind: true,
subtype: 'line',
type: 'line',
padding: 10,
includeDefaultValues: false
});
wall.hasControls = false;
wall.hasBorders = false;
return wall;
}
/**------------------------------ adding the shapes--------------------------------*/
var wall_1 = newWallObject([100, 100, 100, 500]);
var wall_2 = newWallObject([100, 500, 500, 500]);
var wall_3 = newWallObject([500, 500, 500, 100]);
var wall_4 = newWallObject([500, 100, 100, 100]);
var end_point_1 = newCircleObject(wall_1.x1, wall_1.y1, wall_4, wall_1);
var end_point_2 = newCircleObject(wall_2.x1, wall_2.y1, wall_1, wall_2);
var end_point_3 = newCircleObject(wall_3.x1, wall_3.y1, wall_2, wall_3);
var end_point_4 = newCircleObject(wall_4.x1, wall_4.y1, wall_3, wall_4);
wall_1.setStartPointAndEndPoint(end_point_1.name, end_point_2.name);
wall_2.setStartPointAndEndPoint(end_point_2.name, end_point_3.name);
wall_3.setStartPointAndEndPoint(end_point_3.name, end_point_4.name);
wall_4.setStartPointAndEndPoint(end_point_4.name, end_point_1.name);
canvas.add(wall_1, wall_2, wall_3, wall_4, end_point_1, end_point_2, end_point_3, end_point_4);
Add strokeLineCap: 'round',:
function newWallObject(coords) {
var wall = new fabric.LineWithPoints(coords, {
stroke: 'black',
strokeWidth: 6,
lockScalingX: true,
lockScalingY: true,
perPixelTargetFind: true,
strokeLineCap: 'round',
subtype: 'line',
type: 'line',
padding: 10,
includeDefaultValues: false
});
wall.hasControls = false;
wall.hasBorders = false;
return wall;
}
I looked up: http://fabricjs.com/docs/fabric.Object.html#strokeLineCap