I have sub-classed the fabric.Rect "class". It has some properties like the the type, name and area.
var RoomRect = fabric.util.createClass(fabric.Rect, {
initialize: function(options) {
options || (options = { });
this.callSuper('initialize', options);
// type is either one of HALL, BEDROOM, TOILET, etc.
this.set('type', options.type || '');
// name is user defined and also a default name is generated if the name has not been specified
// the name acts as a unique identifier
this.set('name', options.name || '');
// area is in sq. meters
this.set('area', options.area || 0);
},
toObject: function() {
return fabric.util.object.extend(this.callSuper('toObject'), {
type: this.get('type'),
name: this.get('name'),
area: this.get('area')
});
},
_render: function(ctx) {
this.callSuper('_render', ctx);
ctx.font = '1.3em Lato';
ctx.fillStyle = '#383838';
ctx.fillText("Type: "+this.type, -this.width/2+15, -this.height/2 + 20);
ctx.fillText("Name: "+this.name, -this.width/2+15, -this.height/2 + 40);
ctx.fillText("Area: "+this.area+" sq. m.", -this.width/2+15, -this.height/2 + 60);
}
});
When this rectangle is double clicked a prompt is shown, wherein the user can change the name and area properties. If the user clicks on save, following code is triggered. It changes the properties and renders everything again.
function saveRoomInfo(){
if(SELECTED_ITEM == null){return;}
// get the data from the form and put it into the object
var roomInfoForm_name = document.getElementById('roomName');
var newName = roomInfoForm_name.value;
var roomInfoForm_area = document.getElementById('roomArea');
var newArea = roomInfoForm_area.value;
SELECTED_ITEM.set('name',newName);
SELECTED_ITEM.set('area',newArea);
fabricCanvas.renderAll();
}
The problem here is that, the changes are made in the object(confirmed via debugger) but the changes are not done in the user interface(the rectangle object).
Another question, is it a good practice to render everything again. Can I selectively re-render one object. I could delete the old object and render a new one with different properties, but that would require changes in other data structures as well.
In newer versions of fabricjs it wont render all objects, you need to set dirty : true
or put the property in cacheProperties
which affect cache canvas to render the cache. And don't use type
property, as it is used to search the respective class while loading from JSON.
DEMO
fabric.RoomRect = fabric.util.createClass(fabric.Rect, {
type: 'roomRect',
initialize: function(options) {
options || (options = {});
this.callSuper('initialize', options);
// type is either one of HALL, BEDROOM, TOILET, etc.
this.roomRectType = options.roomRectType || '';
// name is user defined and also a default name is generated if the name has not been specified
// the name acts as a unique identifier
this.name = options.name || '';
// area is in sq. meters
this.area = options.area || '';
},
toObject: function() {
return fabric.util.object.extend(this.callSuper('toObject'), {
roomRectType: this.get('roomRectType'),
name: this.get('name'),
area: this.get('area')
});
},
_render: function(ctx) {
this.callSuper('_render', ctx);
ctx.font = '1.3em Lato';
ctx.fillStyle = '#383838';
ctx.fillText("Type: " + this.roomRectType, -this.width / 2 + 15, -this.height / 2 + 20);
ctx.fillText("Name: " + this.name, -this.width / 2 + 15, -this.height / 2 + 40);
ctx.fillText("Area: " + this.area + " sq. m.", -this.width / 2 + 15, -this.height / 2 + 60);
}
});
var canvas = new fabric.Canvas('c');
var roomrect = new fabric.RoomRect({
left: 10,
top: 10,
width: 100,
height: 100,
fill: '',
stroke: 'red'
});
canvas.add(roomrect);
setTimeout(function() {
roomrect.set({
roomRectType: 'room',
name: 'test',
area: 100,
dirty: true
});
canvas.requestRenderAll();
}, 2000)
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.2/fabric.js"></script>
<canvas width=300 height=300 id='c'></canvas>