I am currently creating a grid of Triangles using a for loop using a predetermined set of coordinates in a separate array.
Like so:
function createTri(x, y, z, a) {
var tri = new Kinetic.RegularPolygon({
id: a,
x: x,
y: y,
sides: 3,
radius: 15,
rotation: z,
fillRed: 17,
fillGreen: 17,
fillBlue: 17,
closed: true,
shadowColor: '#5febff',
shadowBlur: 5,
shadowOpacity: 0.18
});
layer.add(tri);
}
for (var i = 0; i < pax.length; i++){
for (var j = 0; j < pax[i].length; j++){
createTri(pax[i][j][0],pax[i][j][1],pax[i][j][2],(i+'')+(j+''));
};
}
However when I try to return a console.log() of a specfic id from the layer container, I am getting nothing but code instead of an id number.
Im not entirely sure if Im calling it wrong for Kinetic, or calling it wrong in general.
So I guess I need to know what the normal way to call the object by the id property would be, or how to properly call it from a Kinetic.Layer.
Edit: to expound, how im trying to call it is defined within the Kinetic documentation as node.find('selector);, so when attempting to find by id, I am trying to log to the console: console.log(layer.find('#1674')); However when I do this, the console logs a prototype object with all possible associated functions instead of the object Im trying to call.
I suspect the source of your problem is that the id of your triangle object is not what you expect it to be.
You should see something like this (in Chrome):
[Kinetic.RegularPolygon, each: function, toArray: function, _init: function, _clearCache: function, _getCache: function…]
Are you seeing this?
[each: function, toArray: function, _init: function, _clearCache: function, _getCache: function…]
That's what I get when I provide a invalid id to the layer's find method.
Here's a fiddle to illustrate: http://jsfiddle.net/klenwell/Tn5bm/
Related
I'm trying to build a random map generator using a grid of tiles in a canvas. For each tile tiles[i], I check properties of the tile before using tiles[i-1]. This seems to work using "ordinary" objects (method 1), but not "2d" objects (method 2), where tiles[i-1].tileID appears as tiles[i].tileID.
for (i=0; i<40; i++) {
tiles[i] = { tileID: i}; // METHOD 1
tiles[i]=c.getContext("2d") // METHOD 2
tiles[i].tileID = i // METHOD 2
if(i>0) {
console.log("my tile ID is", tiles[i].tileID,
". The tile ID of the tile before me is", tiles[i-1].tileID)
}
}
Using method 1 (and deleting the two lines of method 2), the console prints what you'd expect:
my tile ID is 12 . The tile ID of the tile before me is 11
but not for method 2:
my tile ID is 12 . The tile ID of the tile before me is 12
Is there a reason for this (or some other way of doing it I should be adopting) before I look at workarounds? Cheers!
That is because c.getContext("2d") always returns the same object. So when you do it like this:
for (i=0; i<40; i++) {
tiles[i]=c.getContext("2d") // METHOD 2
tiles[i].tileID = i // METHOD 2
}
Expression tiles[i] = c.getContext("2d") assigns the same object to each tile in array. Expression tiles[i].tileID = i is needed to assign a new id to tile object, but tile object always the same. And the same object just get a new ID in each iteration.
The right way to save properties in tile is to make new object for each tile, like you do it in method1:
for (i=0; i<40; i++) {
tiles[i] = { tileID: i}; // METHOD 1
}
I have encountered one of the most bizzare and frustrating behaviours yet. I have sample data:
var nodes = [ //Sample data
{
ID: 1,
Chart: 1,
x: 50,
y: 50,
width: 100,
height: 80,
color: "#167ee5",
text: "Start",
label: "Start",
targets: [2]
},
{
ID: 2,
Chart: 1,
x: 500,
y: 170,
width: 100,
height: 80,
color: "#167ee5",
text: "End",
label: "End",
targets: [3]
},
{
ID: 3,
Chart: 1,
x: 270,
y: 350,
width: 100,
height: 80,
color: "#167ee5",
text: "Mid",
label: "Mid",
targets: []
}
];
for my web application. The issue is with the targets attribute. As you can see it is array. However when I do
console.log(nodes[0]);
and inspect the result in the browser it shows that the value for targets at index 0 is undefined. Same for every other targets that has some values in them (whether 1 or more).
However if I do
console.log(nodes.[0].targets);
it prints out [2]. If I do Array.isArray(nodes[0].targets) it returns false, yet if I do console.log(nodes[0]) and inspect the result in the browser console, it shows that the object prototype is in fact Array and simply the value at index 0 is undefined.
It worked the day before and now it doesn't. The only thing I did was I restructured the object that uses this variable later. But the console log is being called before the object is even instantiated for the first time (and it doesn't change the nodes var anyway, only reads it).
Does anyone have any clues as to what might be causing this behaviour. If it helps I am using Paperscript and this code runs in the paperscript scope (as it did before when everything worked fine).
UPDATE
Ok after more blind debugging I have determined the block of code that causes the issue, how or why is completely beyond me.
Basically I define an object constructor beflow. The constructor loops through the nodes, makes Paperscript shapes and adds the targets to the arbitrary data attribute of the paperJS path object:
function Flowchart(nodes, chartdata) {
//Member vars. They are only used internally
var connections = [];
var shapes = [];
var connectors = [];
//Constructor operations
createShapes(nodes); //If I uncomment this, the problem goes away
//...
function createShapes(nodes) {
nodes.forEach(function (node) { //for each node data entry
console.log(node); //At this point, the targets are screwed up already
var point = new Point(node.x, node.y); //make a PaperJS point for placement
var size = new Size(node.width, node.height); //make a PaperJS size object
var shape = makeRectangle(point, size, 8, node.color); //Pass to the object instantiating function
shape.data = { //Store arbitrary data for programming reference.
ID: node.ID,
label: node.label,
text: node.text,
'connectors': {
to: [],
from: []
},
targets: node.targets //this is undefined
};
console.log(node.targets) //this logs [2] or [3] but not Array[1]...
shapes.push(shape); //Store reference for later
});
shapes.forEach(function (shape) { //loop though all drawn objects
if (shape.data.targets.length > 0) { //if shape has targets
var targets = _.filter(this.shapes, function (target) {
return _.contains(shape.data.targets, target.data.ID);
});
for (var i = 0; i < shape.data.targets.length; i++) {
shape.data.targets[i] = targets[i]; //Replace the ID-type reference with drawn object reference
}
}
});
}
//... The rest of the object
}
console.log(nodes);
//It doesnt seem to matter whether i put this before or after instantiating.
//It doesnt even matter IF I instantiate in the first place.
var chart = new Flowchart(nodes, chartdata);
This behaviour has been caused by the changes to how Chrome treats enumerable properties of objects. Because Chrome updates silently, it's impossible to notice.
It must have been causing me a lot of headache if I remembered the cause after all this time... (Also it's embarrassing how bad I was at writing questions, but I guess that I realise it means I have progressed since then somewhat).
If I set a variable like this:
var coords = jcrop_api.tellSelect();
It returns my current Jcrop selections coordinates in an x,y,x2,y2,h,w format.
Now if I want to set my coordinates back to that, I could go:
jcrop_api.animateTo(coords)
But the animateTo function only takes an array of 4, [x, y, w, h]
When I try to do the above way, it eventually breaks the code.
So how do I change my variable coords to fit this format?
Thanks
The API functions you mention at least at the 0.9.12 version of the plugin..
jcrop_api.tellSelect() returns an object like this:
{
x: 0,
y: 0,
x2: 10,
y2: 10,
w: 10,
h:10
}
and jcrop_api.animateTo needs one array like [x,y,x2,y2], so, try this:
var c = jcrop_api.tellSelect();
jcrop_api.animateTo([c.x,c.y, c.x2, c.y2]);
With a lack of math neurons in my brain, i struggle a bit in finding my way around this.
I need to create a simple javascript function that will receive three parameters:
A one-dimensional, normal indexed Array with X elements (the values being unique IDs)
A target ID to select
An amount of elements to return
The third parameter would ask the function to return a set of elements, with the element having the target ID being either in the center of the result, or next to it.
The result of the function should be an array as well.
A few examples to make it a more visual explanation:
function([100,120,140,160,180,200], 120, 3)
// should return [100,120,140]
function([100,120,140,160,180,200], 160, 4)
// should return [140,160,180,200]
function([100,120,140,160,180,200], 180, 5)
// should return [140,160,180,200,100]
The case covered by the last example is what confuses me while writing the code, which i am currently attempting to, but i find myself writing strange conditions, numerous if-statements and code that generally seems like a work-around. Also the cases of parameter 3 being larger than the amount of elements in parameter 1 are a bit of an over-brainer for me.
I feel unsafe continuing with this code, because it feels buggy and simply not proper. Surely somebody with proper math skills could provide me with the theory i need to understand how to accomplish this in a more elegant fashion.
Theory or pseudo-code will suffice, but if someone has something like this ready at hand, please don't hesitate to share it.
Thank You!
(Here is what i have written so far - based on the prototype JS class implementation)
var CBasicMatrix=Class.create({
initialize: function(elementList){
this.elementList=elementList;
},
select: function(id, amount){
if(amount>this.elementList.length)
amount=this.elementList.length;
if(!this.elementList.length) return false;
var elementIndex=this.elementList.indexOf(id);
if(elementIndex==-1) return false;
var isRound=amount%2==0;
var amountHalf=isRound ? (amount/2) : (Math.ceil(amount/2)-1);
// [464,460,462,461,463]
var result=[];
if(elementIndex-amountHalf >= 0) {
var startIndex=(elementIndex-amountHalf);
for(i=startIndex;i<=startIndex+amount;i++){
result.push(this.elementList[i];
}
} else {
// more seemingly stupid iterative code coming here
}
}
});
Edit: In order to make this more understandable i will state the purpose. This code is supposed to be used for kind of a slideshow, in which multiple elements (parameter 3) are visible at the same time. Parameter 1 is the list of (the IDs of the) total elements in their correct order as they appear in the HTML declaration. Parameter 2 is the element that is currently selected and therefore should appear in the middle.
Here is my solution:
function method(arr, value, n) {
var result = [],
len = arr.length,
index = arr.indexOf(value);
for (var i = 0; index > -1 && i < n ; i++) {
result.push(arr[(len + index - ~~(n / 2) + (n % 2 ^ 1) + i) % len]);
}
return result;
}
TESTS:
var arr = [100, 120, 140, 160, 180, 200];
method(arr, 120, 3); // [100, 120, 140]
method(arr, 160, 4); // [140, 160, 180, 200]
method(arr, 180, 5); // [140, 160, 180, 200, 100]
method(arr, 100, 3); // [200, 100, 120]
I will help you by providing a pseudo code :
1 . if there is no match you should return an empty array.
2 . if there is a match you just divide the third parameter by 2, you take the result , you loop from the element found's index minus the previous result until the third parameter's value and you store the elements in a new array.
3 . you return the new array.
Update:
I saw your code and I don't see any problem with it.
After some careful debugging and overthinking my approach, i managed to find a solution that seems proper and safe. I am sure this could be optimised further and if anyone has any suggestions, feel free to share them.
var CBasicMatrix=Class.create({
initialize: function(elementList){
this.elementList=elementList;
},
select: function(id, amount){
if(amount>this.elementList.length)
amount=this.elementList.length;
if(!this.elementList.length) return false;
var elementIndex=this.elementList.indexOf(id);
if(elementIndex==-1) return false;
var isRound=amount%2==0;
var amountHalf=isRound ? (amount/2) : (Math.floor(amount/2));
var result=[];
var startIndex=(elementIndex-amountHalf);
var endIndex=(startIndex+amount-1);
var targetIndex=0;
for(i=startIndex;i<=endIndex;i++){
targetIndex=i;
if(i>this.elementList.length-1) targetIndex=i-this.elementList.length;
if(i<0) targetIndex=i+this.elementList.length;
result.push(this.elementList[targetIndex]);
}
return result;
}
});
I have the following piece of code written using mootools and mootools canvas library.
CANVAS.init({ canvasElement : 'canvas', interactive : true });
var itemlayer = CANVAS.layers.add({ id : 'items' });
for(var j = 0; j < 5; j++)
{
for(var i = 0; i < 5; i++)
{
itemlayer.add({
id : 'item-'+i + '-' + j,
x : 51 * i,
y : 51 * j,
w : 50,
h : 50,
state : 'normal',
interactive : true, //although they have no interactive events!
colors : { normal : '#f00', hover : '#00f' },
events : {
onDraw : function(ctx){
ctx.fillStyle = this.colors[this.state];
ctx.fillRect(this.x,this.y,this.w,this.h);
this.setDims(this.x,this.y,this.w,this.h);
}
}
});
}
}
CANVAS.addThread(new Thread({
id : 'myThread',
onExec : function(){
CANVAS.clear().draw();
}
}));
Now what I would like to do is destroy the squares soon after they are drawn on the canvas.
The function given in the documentation to do so is
item.destroy( );
Refer here.
The problem is, no matter how I do it, I am not able to destroy the objects from the canvas! What would be the correct way to do it?
Refer code here implemented on js fiddle.
Mootools is a class based JavaScript framework. In order to use it you need to call objects as if they were classes and not their constructors directly.
The CANVAS library is an exception to the rule as it is a "static" class. However, before you can use layer methods, you need to initialize the layer class.
When you use the add method of the Layer class, you are asking for new items to be added to that Layer class. You use this method twice, once just before the loop and once just inside it. However, at no point do you initialize the layer class. Therefore, I assume that case before the loop is supposed to initialize the class. This needs to be replaced with var itemlayer = new Layer('items');
When using itemlayer.add inside the loop, you are passing and object to the Layer object which is then automatically creating the CanvasItem objects for you. It then returns these objects to you. Since the destroy method is a member of the CanvasItem class, you need to catch them in a variable in order to call it. Since it is happening inside a loop, if you wish to delete the objects outside of the loop, you will need an array.
CANVAS.init({ canvasElement : 'canvas', interactive : true });
var itemlayer = new Layer('items');
var itemArray = new Array;
for(var j = 0; j < 5; j++)
{
for(var i = 0; i < 5; i++)
{
item Array[j][i] = itemlayer.add({
id : 'item-'+i + '-' + j,
x : 51 * i,
y : 51 * j,
w : 50,
h : 50,
state : 'normal',
interactive : true, //although they have no interactive events!
colors : { normal : '#f00', hover : '#00f' },
events : {
onDraw : function(ctx){
ctx.fillStyle = this.colors[this.state];
ctx.fillRect(this.x,this.y,this.w,this.h);
this.setDims(this.x,this.y,this.w,this.h);
}
}
});
}
Then when you wish to destroy an item, as long as you know its j and i index, you can delete it with itemArray[j][i].destroy().
Finally, keep in mind that the destroy method is only documented as firing the CanvasItem's destroy event. Depending on what the library has implemented, you may need to write your own destroy event in order to actually remove it from the canvas.
I'm not familiar with the innards of mootools, but it looks like item.destroy() merely dispatches an event rather than actually destroys the item on the canvas. The canvas is merely a bitmap - it doesn't have layers or records of individual primitives like SVG does.
You would need to actually destroy the rectangles yourself by redrawing the canvas without them, drawing over them, or use:
ctx.clearRect(this.x, this.y, this.w, this.h);
This will clear all info on the canvas within the rectangle defined by the parameters.
edit: After doing a little reading, it looks like you pass a destroy() function to the constructor of your canvas item. This function must contain whatever code you want to destroy your item (clearRect, fillRect, whatever).