In order to get fabricjs canvas image by name, I need to set unique id or name to this iamge. I've created a new class fabric.NamedImage
That's how I did it.
fabric.NamedImage = fabric.util.createClass(fabric.Image, {
type: 'nameimage',
initialize: function (element, options) {
this.callSuper('initialize', element, options);
options && this.set('name', options.name);
},
toObject: function () {
return fabric.util.object.extend(this.callSuper('toObject'), { name: this.name });
},
_render: function (ctx) {
this.callSuper('_render', ctx);
}});
And fromObject
fabric.NamedImage.fromObject = function (object, callback) {
fabric.util.loadImage(object.src, function (img) {
var instance = new fabric.NamedImage(img, object);
callback && callback(instance);
});
};
fabric.NamedImage.async = true;
But when I'm trying to load canvas loadFromJSON for some reasons I keep getting error
cannot read property 'async' of undefined
Here is code where I'm trying to load JSON
for (i = 0; i <= canvas.length; i++) {
JSON.parse(imageQuery[i]);
canvas[i].loadFromJSON(imageQuery[i]);
canvas[i].renderAll();
console.log(' this is a callback. invoked when canvas is loaded!xxx ');
}
I've already read
cannot read property 'async' of undifined
and
save canvas to server with custom attribute
Is there some way to get object by name?
From the official docs,
var rect = new fabric.Rect();
rect.toObject = (function(toObject) {
return function() {
return fabric.util.object.extend(toObject.call(this), {
name: this.name
});
};
})(rect.toObject);
canvas.add(rect);
rect.name = 'trololo';
console.log(JSON.stringify(canvas));
and the logged output will be
'{"objects":[{"type":"rect","left":0,"top":0,"width":0,"height":0,"fill":"rgb(0,0,0)","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0,"name":"trololo"}],"background":"rgba(0, 0, 0, 0)"}'
Please refer this http://fabricjs.com/fabric-intro-part-3
Cheers!
Plesae change the type property of your class to 'namedImage' and it should go.
You can add custom properties to different objects in fabric js using the code given below:
obj.toObject = (function (toObject) {
return function () {
return fabric.util.object.extend(toObject.call(this),{
id : value
});
};
})(obj.toObject);
Here obj is the canvas object that you have created and id is the custom attribute we are adding to this object.
Suppose you have saved canvas in the form of a json file. So while using json.stringify consider the code below:
json_data = JSON.stringify(self.canvas.toJSON(['attribute1',
'attrubute2']));
Here in square brackets include all the custom attributes. Later on when you will use loadfromjson all the custom properties will automatically be included. Hope it will work. Have a nice day!!
Related
When trying to put following Script into a CEWP on a Listview:
; (function ()
{
var fieldJsLinkOverride = {};
fieldJsLinkOverride.Templates = {};
fieldJsLinkOverride.Templates.Fields =
{
'Title': { //Titelfeld
'View': function () {
return ''+ctx.CurrentItem.Title+''
}
}
};
// Register the rendering template
SPClientTemplates.TemplateManager.RegisterTemplateOverrides(fieldJsLinkOverride);
})();
So this changes the Title field into my desired link, this works all fine, but when i try to sort the list, it returns following:
Unable to get property 'ItemID' of undefined or null
Does anyone know this problem or might find a solution for it?
You are using ctx in your code but you have not passed it to your function..
Remember Each of the functions you write for JS Link will be passed a “context” object as a parameter. From this object you can retrieve properties about the current list, current list item and other objects.
Refer below code. Here we are passing ctx to a function. Try this :
{
'Title': { //Titelfeld
'View': function (ctx) {
return ''+ctx.CurrentItem.Title+''
}
}
};
I am trying to make a preset list of options that are allowed in my object list. Here is code
var a = function(cmd, options){
var objList = [options.search ,options.demand];
if(!(options in objList)){
console.warn('Not an Allowed * in the options Property');
}
};
or should I do
var a = function(cmd, options){
var objList = [search , demand];
if(!(options in objList)){
console.warn('Not an Allowed option in the options Property');
}
};
Basically what I want to do is set that search and demand are allowed options in the options Property so later than can do
a('cmd',{
search:'',
demand:function() {
alert('Hello');
},
//warn that the next option is not allowed
quote: function() {
alert('quote of user');
}
});
If you are having trouble understanding what I am asking please ask and I will do my best to explain a bit more.
maybe writing it like so would be better?
var a = function(cmd, options){
options = {
theme: function(color) {
$('body').css('backgroundColor',color);
},
color:''
};
};
a('cmd',{
theme:'#000'//though this is not working?
});
You could check each property in options against an array of allowed options like this:
var a = function(cmd, options){
var allowedOptions = ["search", "demand"];
var hasDisallowedOptions = false;
for (option in options) {
if(allowedOptions.indexOf(option) === -1 ) {
hasDisallowedOptions = true;
break;
}
}
// if hasDisallowedOptions is true, then there is a disallowed option
};
jsfiddle with a couple test cases/examples
A one idea of passing arguments in an object is, that it allows you to choose which argument you want to use in a function, you can simply ignore extra properties in the options object. Hence you don't need to "filter" the properties of the argument either.
Let's assume you've a function like this:
var a = function (cmd, options) {
var theme = {
backgroundColor: options.bgColor,
color: options.color
}
// Do something with theme
// Notice also, that there was no use for options.extra in this function
}
Then you invoke a like this:
a('cmd', {
bgColor: '#ff0',
color: '#000',
extra: 'This is an extra property'
});
Now you can see, extra is not used in a at all, though it was a property of the anonymous object passed to a as an argument. Also all arguments passed to a are garbage collected, unless you're not going to create a closure, i.e. returning a local value or a function from a.
I've a page that is generated dynamically, and that includes certain number (user-dynamically-defined) of advanced scatter plot charts. I intend to create a JavaScript object which defines the scatter plot itself, i.e. which takes some parameters, some data, and some container ID, and which will create the various elements needed to obtain the visualisation: canvas elements, toolbar, etc.. To do so, I started with the following (simplified) class:
(function () {
if (!this.namespace) { this.namespace = {};}
this._instances = { index: 0 };
this.namespace.ScatterPlot = function (containerId, file, options) {
_instances.index ++;
this.id = this.containerId+"-"+_instances.index ;
this.containerId = containerId ;
_instances [this.id] = this;
// ... Do stuffs with file and options ...
// Initialize elements once the DOM is ready
$(this.updateDOM);
}
namespace.ScatterPlot.prototype = {
updateDOM: function() {
$("<canvas>")
.click(clickCallback)
.appendTo("#"+this.containerId);
//(...)
},
clickCallback: function() {
alert("Some click: "+this.id);
}
}
})();
Each object can be created with:
var v1 = new namespace.ScatterPlot("container1", "foo", "foo");
var v2 = new namespace.ScatterPlot("container2", "foo", "foo");
There are two problems here: (1) in updateDOM, 'this' does not make reference to my initial ScatterPlot object, which means that this example will never work, and (2) similarly, the clickCallback will not be able reference the scatterplot with 'this' either.
I'm new to javascript, and I'm still struggeling to understand the logic of OO programming in javascript, so the question is: I'm I taking the wrong direction here ? After some digging, I could roughly achieve what I wanted by passing this to updateDOM:
$(this.updateDOM(this)); // This blows my eyes but does the trick, at least partially
updateDOM: function(that) {
$("<canvas>")
.click(that.clickCallback)
.appendTo("#"+that.containerId);
//(...)
},
clickCallback: function() {
// Not working either... Should pass 'that' to the function too
alert("Some click: "+this.id);
}
But I don't feel this patters to be very elegant... And the problem is not fixed either regarding the click callback.
Thoughts ?
Have a look at MDN's introduction to the this keyword.
The standard ways of dealing with that issue are using a that variable - not as an argument, but in a separate function:
var that = this;
$(function() {
that.updateDOM();
});
// or
$(this.getClickCallback());
...
namespace.ScatterPlot.prototype.getClickCallback = function() {
var that = this;
return function clickCallback(e) {
alert("Some click: "+that.id);
};
};
Alternatively, you can always use .bind() (or $.proxy for older browsers) which do quite what the second example does in a more generic way:
$(this.clickCallback.bind(this));
I have a problem of loading the image filter that is saved to my subclassed image object. Here is the code for my subclass:
fabric.TopImage = fabric.util.createClass(fabric.Image, {
type: 'top-image',
initialize: function (element, options) {
this.callSuper('initialize', element, options);
this.set('name', 'top');
this.set('lockUniScaling', true);
},
toObject: function () {
return fabric.util.object.extend(this.callSuper('toObject'), { name: this.name });
}
});
fabric.TopImage.fromObject = function (object, callback) {
fabric.util.loadImage(object.src, function (img) {
callback && callback(new fabric.TopImage(img, object));
});
};
fabric.TopImage.async = true;
This works fine without applying any filters.
I add a filter to an instance of this subclass by using something like:
objectToFilter.filters[0] = new fabric.Image.filters.RemoveWhite({threshold: 20, distance: 20});
objectToFilter.applyFilters(canvas.renderAll.bind(canvas));
Saving the object passes the filter object with a type, threshold and distance.
However loading this object causes an error in the all.js file of:
Uncaught TypeError: Object #<Object> has no method 'applyTo'
Since you never call the Image.fromObject function there, filters are never initialized and they can't be applied. In order to get filters to work out as expected, use this function instead:
fabric.TopImage.fromObject = function (object, callback) {
fabric.util.loadImage(object.src, function (img) {
var oImg = new fabric.TopImage(img);
oImg._initConfig(object);
oImg._initFilters(object);
callback(oImg);
});
};
I have the following (simplified) object literal. The icons method uses closure to hide the icons variable, which I'd like to have as an associative array for later lookups.
var MapListings = {
icons: function () {
var allIcons = [] ;
return {
add: function (iconType, iconImage) {
var icon = new GIcon(MapListings.baseIcon);
icon.image = iconImage;
allIcons[iconType] = icon; // fails, but this is what I want
// allIcons.push(icon); // works, but this is not what I want
},
get: function () {
return allIcons;
}
};
} ()
}
I add items to the to the icons object like so:
MapListings.icons.add("c7", "/images/maps/blue.png");
MapListings.icons.add("c8", "/images/maps/red.png");
The following doesn't work:
allIcons[iconType] = icon;
But this does:
allIcons.push(icon);
Outside of the closure the associative array style works fine, so perhaps there is a conflict with jQuery? The error I get in firebug a is undefined looks to come from the library. I'd like to maintain the associative array style.
Any ideas?
Update
It looks like this conflict is coming from google maps. Odd, not sure of a way around this.
Dumbass Update
The part of my object literal that returned a base GIcon() object wasn't returning an object at all. So, the object didn't have the right properties.
baseIcon: function () {
var base = new GIcon();
base.shadow = '/images/maps/shadow.png';
base.iconSize = new GSize(12, 20);
base.shadowSize = new GSize(22, 20);
base.iconAnchor = new GPoint(6, 20);
base.infoWindowAnchor = new GPoint(5, 1);
return base;
}
And MapListings.baseIcon is NOT the same as MapListings.baseIcon()! D'oh
if you want a lookup table, just do var allIcons = {}
EDIT: Though technically it should work either way, as an array IS an object. Are you sure there isn't more to this?
EDIT #2: Can't you just make allIcons as a property of MapListings?
EDIT #3: I think it's working, but maybe you're not accessing it right? That or it fails creating the object with Google somehow, or the error you posted is happening elsewhere, and not here
function GIcon(){};
var MapListings = {
icons: function () {
var allIcons = [] ;
return {
add: function (iconType, iconImage) {
var icon = new GIcon(MapListings.baseIcon);
icon.image = iconImage;
allIcons[iconType] = icon; // fails, but this is what I want
// allIcons.push(icon); // works, but this is not what I want
window.x = allIcons
},
get: function () {
return allIcons;
}
};
} ()
};
MapListings.icons.add("c7", "/images/maps/blue.png");
MapListings.icons.add("c8", "/images/maps/red.png");
alert( MapListings.icons.get()['c8']['image'] )
You shouldn't loop using .length but instead directly access c7 or c8.
x = MapListings.icons.get();
for ( var prop in x ) {
if ( x.hasOwnProperty(prop ) ) {
alert( x[prop]['image'] )
}
}
So one thing you could do to fix this is change the way you reference the array. Since external to your add method you do this:
MapListings.icons["c7"]
You can also just use this to add to your array inside your add function:
add: function (iconType, iconImage) {
MapListings.icons[iconType] = iconImage;
},
allIcons[iconType] = icon; fails because allIcons is an Array, not an object. Try initializing allIcons to {} instead. That would allow you to place items in the collection by key.