Simple variable issue - javascript

I am trying to change my variables inside my items variable and not sure how to do it.
var items = [
{name: '#builder', x: 0, y:15},
{name: '#image', x: 0, y:15},
{name: '#button', x: -100, y: -55}
];
//I only want to change builder and image but not button...How to do this?
if(i>5){
items = [
{name: '#builder', x: 50, y:105},
{name: '#image', x: 110, y:115}
];
}
Thanks for the help.

Have you considered using an object hash table instead?
var items = {
'#builder' : { x: 0, y:15},
'#image': { x: 0, y:15},
'#button': {x: -100, y: -55}
};
Then you can change them like so:
items['#builder'].x = 50;
If it's not an option, that's cool, but it seems like if you want to look things up by name, this might be a better route for you.
Outside of that, you'd have to loop through each record to find the name you wanted and set the values appropriately.
var items = [
{name: '#builder', x: 0, y:15},
{name: '#image', x: 0, y:15},
{name: '#button', x: -100, y: -55}
];
function updateValues(name, x, y) {
for(var i = 0; i < items.length; i++) {
var item = items[i];
if(item.name == name) {
item.x = x;
item.y = y;
return;
}
}
}
updateValues('#builder', 50, 105);

If you know the position, then you could do:
if (i > 5) {
items[0].x = 50;
items[0].y = 105;
items[1].x = 110;
items[1].y = 115;
}

Related

Konva.js is very laggy on mobile devices

I want to start using Konva.js library. It looks really great in my browser (chrome) on my PC and has awesome functionality. I created code to make some really easy animations (move and rotate). But when I tried to run my webpage on a mobile device or even in Safari browser, it started to be really laggy. Moreover, when I set four objects(Images) into a movement, the browser crashed.
I did some tests and realized that even draggable objects are laggy on mobile devices. (When I tried to move them, they had really slow and jerky movement).
Is there some way to optimize it. (I have tried the recommended batchDraw() function, but it didn't help)? How can I make the movement fluent? If this is not currently possible, will there be some optimization possibilities in the future?
Here is my code... but obviously you have to run it on a mobile phone to see the effect. To activate the effect click(touch) on images.
http://bannerteam.tode.cz/konvaAnim/
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Konva animace</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #F0F0F0;
}
</style>
<script src="https://cdn.rawgit.com/konvajs/konva/2.0.3/konva.min.js"></script>
</head>
<body>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;
var lengthOfAnim = 60;
var pos = [{x: 50, y: 50, p: 'A'}, {x: 250, y: 50, p: 'B'}, {x: 450, y: 50, p: 'C'},
{x: 50, y: 250, p: 'D'}, {x: 250, y: 250, p: 'E'}, {x: 450, y: 250, p: 'F'},
{x: 50, y: 450, p: 'G'}, {x: 250, y: 450, p: 'I'}, {x: 450, y: 450, p: 'J'},
{x: 50, y: 650, p: 'K'}, {x: 250, y: 650, p: 'F'}];
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
var imageObj = [];
var img = new Image();
var doAnimations = function(i){
switch(i){
case 0:
imageObj[i].img.opacity(1 - imageObj[i].time/lengthOfAnim);
break;
case 1:
if(imageObj[i].time === lengthOfAnim -1)
imageObj[i].img.y(imageObj[i].img.y() + (lengthOfAnim-1)*2);
else
imageObj[i].img.y(imageObj[i].img.y() - 2);
break;
case 2:
imageObj[i].img.opacity(1 - imageObj[i].time/lengthOfAnim);
if(imageObj[i].time === lengthOfAnim -1)
imageObj[i].img.y(imageObj[i].img.y() + (lengthOfAnim-1)*2);
else
imageObj[i].img.y(imageObj[i].img.y() - 2);
break;
case 3:
var parent = imageObj[i].img.getParent();
parent.clipFunc(function(ctx) {
ctx.rect(imageObj[i].img.x() - 75, imageObj[i].img.y() - 75, 75*(1 - imageObj[i].time/lengthOfAnim), 150);
ctx.rect(imageObj[i].img.x() + 75 - 75*(1 - imageObj[i].time/lengthOfAnim) , imageObj[i].img.y() - 75, 75*(1 - imageObj[i].time/lengthOfAnim), 150);
});
break;
case 4:
var parent = imageObj[i].img.getParent();
parent.clipHeight(150*(1 - imageObj[i].time/lengthOfAnim));
break;
case 5:
imageObj[i].img.opacity(1 - imageObj[i].time/lengthOfAnim);
imageObj[i].img.rotation(90*(imageObj[i].time/lengthOfAnim));
break;
case 6:
imageObj[i].img.opacity(1 - imageObj[i].time/lengthOfAnim);
var pom = (1 - imageObj[i].time/lengthOfAnim);
imageObj[i].img.scale({x: pom, y: pom});
break;
case 7:
if(imageObj[i].time === lengthOfAnim -1)
imageObj[i].img.x(imageObj[i].img.x() - (lengthOfAnim-1)*2);
else
imageObj[i].img.x(imageObj[i].img.x() + 2);
imageObj[i].img.move({x: 0, y: 10*(Math.sin(6*Math.PI*(imageObj[i].time/lengthOfAnim)))});
break;
case 8:
imageObj[i].img.rotation(20*Math.sin(6*Math.PI*(imageObj[i].time/lengthOfAnim)));
break;
case 9:
imageObj[i].img.opacity(0.5 + Math.abs((imageObj[i].time/lengthOfAnim-0.5)));
break;
case 10:
imageObj[i].img.draggable(true);
break;
}
}
img.onload = function() {
for(let i = 0; i < pos.length; i++){
var layer = new Konva.Layer();
var yoda = new Konva.Image({
x: pos[i].x + 75,
y: pos[i].y + 75,
image: img,
width: 150,
height: 150,
offset: {
x: 75,
y: 75
}
});
imageObj.push({img: yoda, layer: layer, time: 0});
var charac = new Konva.Text({
x: pos[i].x + 50,
y: pos[i].y + 160,
text: pos[i].p,
fontSize: 30,
fontFamily: 'Calibri',
fill: 'black'
});
if(i === 3){
var group = new Konva.Group({clipFunc:function(ctx) {
ctx.rect(pos[i].x, pos[i].y, 150, 150)
},});
group.add(yoda);
layer.add(group);
}else if(i === 4){
var group = new Konva.Group({clip: {
x : pos[i].x,
y : pos[i].y,
width : 150,
height : 150
},});
group.add(yoda);
layer.add(group);
}else
layer.add(yoda);
layer.add(charac);
stage.add(layer);
yoda.on('click tap', function() {
if(imageObj[i].time === 0)
imageObj[i].time = lengthOfAnim;
});
}
setInterval(function(){
for(var i = 0; i < pos.length; i++){
if(imageObj[i].time > 0){
imageObj[i].time--;
doAnimations(i);
imageObj[i].layer.draw();
}
}
}, 40);
}
img.src = './castle.png';
</script>
<body>
</html>
There are a lot of ways to improve the performance.
You can read a lot of tips here: https://konvajs.github.io/docs/performance/All_Performance_Tips.html
Your stage looks simple, so performance should be very good on mobile.
Some tips:
Do not use setInterval. Use requestAnimationFrame. The animation will be much smoother
use layer.batchDraw()
If possible try to move animated objects into another layer, so you don't need to redraw ALL nodes

By modifying arr[1][0] multiple cells are modified

I'm trying to make a match-3 game (candy crush like). I have an object level which has tiles property which is a 2d array. After I do some manipulations I want to change the type of a specific element to -1 using this simple line (I'll be using for, but for now I've made it simple for demonstrative purposes)
level.tiles[1][0].type = -1;
Here is the code
var level = {
x: 250, // X position
y: 113, // Y position
columns: 8, // Number of tile columns
rows: 8, // Number of tile rows
tilewidth: 40, // Visual width of a tile
tileheight: 40, // Visual height of a tile
tiles: [], // The two-dimensional tile array
selectedtile: {selected: false, column: 0, row: 0}
};
var tileTypes = [
{
type: "red",
colors: [255, 128, 128]
},
{
type: "green",
colors: [128, 255, 128]
},
{
type: "darkBlue",
colors: [128, 128, 255]
},
{
type: "yellow",
colors: [255, 255, 128]
}
];
function createLevel() {
for (var i = 0; i < level.columns; i++) {
level.tiles[i] = [];
}
for (var i = 0; i < level.columns; i++) {
for (var j = 0; j < level.rows; j++) {
level.tiles[i][j] = getRandomTile();
}
}
}
function getRandomTile() {
return tileTypes[Math.floor(Math.random() * tileTypes.length)];
}
createLevel();
level.tiles[1][0].type = -1;
Unfortunately not only tiles[1][0] is modified, but multiple cells. The interesting part is that every time random cells are affected
This occurs because getRandomTile() returns a reference to a tile type, not a copy of it.
I.e. to simplify this case:
var a = {x: 1};
var b = [a, a, a, a];
b[0].x = 2;
console.log(a, b);
will output
{x: 2} [{x: 2}, {x: 2}, {x: 2}, {x: 2}]
If you want the tiles to be modifiable, have getRandomTile return a copy – a shallow copy in this case, so colors is still a reference, not a copy – of the randomly chosen tile type.
function getRandomTile() {
const tileType = tileTypes[Math.floor(Math.random() * tileTypes.length)];
// Idiom for shallow copy, i.e. assign all properties of tileType
// into a new, unique object.
return Object.assign({}, tileType);
}
The problem is you modify the type object, instead of linking to another type. A solution would be to clone it when creating the tiles:
function getRandomTile() {
var srcType = tileTypes[Math.floor(Math.random() * tileTypes.length)];
return {type:srcType.type, colors:srcType.color};
}
Another one (depending on your goal) would be to have Tile objects, each one having a reference to a Type object (not just an integer). At this point some classes might be helpful:
class TileType {
constructor(colors){
this.colors = colors;
}
}
let tileTypes = [...]
class Tile {
constructor(){
this.type = tileTypes[Math.random()*tileTypes.length|0];
}
setNewType(type){
this.type = type;
}
}
etc.
This is caused by getRandomTile which returns the same reference of the object defined in tileTypes if the index passed in is the same. You can print tileTypes to help your understand what happens.

Plot part of the map underneath a surface

I would like to visualize my geographic data using plotly (javascript). I have plotted a surface and some datapoints above it as follows:
Here is my code to do it:
var x_completed = [];
for (var i=0;i < column_count;i++) {
x_completed.push(full_data_list[i].lng);
}
var y_completed = [];
for (var i=0;i < row_count;i++) {
y_completed.push(full_data_list[i*column_count].lat);
}
var z_completed = [];
for (var i = 0; i < row_count; i++) {
row = [];
for (var j = 0; j < column_count; j++) {
row.push(full_data_list[i*column_count+j].p_val);
}
z_completed.push(row);
}
x_org = [];
y_org = [];
z_org = [];
for (var i = org_data.length - 1; i >= 0; i--) {
dp = org_data[i];
x_org.push(dp.lng);
y_org.push(dp.lat);
z_org.push(dp.p_val);
}
var trace = {
x: x_org,
y: y_org,
z: z_org,
mode: 'markers',
name: 'Original Measurement',
marker: {
size: 5,
line: {
color: 'rgba(217, 217, 217, 0.14)',
width: 0.5},
opacity: 0.8},
type: 'scatter3d'
};
var data_z = {x: x_completed, y: y_completed, z: z_completed, type: 'surface', name: 'Inferred Data using Matrix Completition'};
var layout = {
scene: {
xaxis:{title: 'Latitude'},
yaxis:{title: 'Longitude'},
zaxis:{title: 'CO2'},
},
autosize: true,
margin: {
l: 0,
r: 0,
b: 50,
t: 50,
pad: 4
},
}
Plotly.newPlot('map', [data_z,trace],layout);
Now my problem is that I want to draw the corresponding geographic area underneath the surface so everyone can easily know where the data comes from. I have tried googling but I couldn't figure out how to include an 2D image (or part of the map) to a 3D environment. I have had a look at scattermapbox but it only allows 2D map. Could you please suggest me?
Many thanks.

Creating a grid array of co ordinates

I'm a little confused with my array creation. I have a list of items with x:y co ordinates and also their dimensions (in grid sizes) ... for example:
x: 1
y: 7
width: 2 tiles
height: 2 tiles
So the idea im trying to do is create an array of x:y grids that are "occupied". But looping such data so the occupied tiles would there for be:
x: 1
y: 7
//
x: 2
y: 7
//
x: 1
y: 8
//
x: 2
y: 8
Because in the above example the item is 2 by 2 tiles ( a square ).
My object is structured like this (shown in console.log(sdata);)
7: Array[4]
0: "1"
1: "7"
2: "2"
3: "2"
So yeah im trying to make an array to store this kind of grid reference..Hope I explained what I trying to get at, but i can't work out how to structure a loop to create the array.
http://jsfiddle.net/rlemon/JeeV2/ Here is an example of how you could produce this type of 'grid' occupied data..
var chords = [ // instead of arrays we'll use objects.. they're nicer.
{
x: 1,
y: 7,
h: 2, // height
w: 2}, // width
{ // ohh look a second plot point.
x: 4,
y: 1,
h: 3,
w: 2},
];
var occupied = { // will be your cells points occupied
x: [],
y: []
};
chords.forEach(function(chord) { // now lets loop the chords and produce the occupied array
occupied.x.push( chord.x );
occupied.x.push( chord.x + (chord.w-1) ); // expand the width of the cell - initial point
occupied.y.push( chord.y );
occupied.y.push( chord.y + (chord.h-1) ); // expand the height of the cell - initial point
});
console.log( occupied );
// outputs
​Object
x: Array[4]
0: 1
1: 2
2: 4
3: 5
y: Array[4]
0: 7
1: 8
2: 1
3: 3
The resulting outputArray array is a collection of objects in the form { x: value, y: value}.
var inputArray = [ [1,7,2,2], ... ];
var outputArray = [];
for(var i = 0; i < inputArray.length; i++) {
var item = {
x: inputArray[i][0],
y: inputArray[i][1],
width: inputArray[i][2],
height: inputArray[i][3]
};
for(var xx = 0; xx < item.width; xx++) {
for(var yy = 0; yy < item.height; yy++) {
outputArray.push({
x: item.x + xx,
y: item.y + yy
});
}
}
}
​I added the x, y, width and height attributes to make it more understandable.
function range(a, b) {
/*
range(5) -> [0,1,2,3,4]
range(1,4) -> [1,2,3]
*/
if (b===undefined)
var start=0, stop=a;
else
var start=a, stop=b;
var R = [];
for(var i=start; i<stop; i++)
R.push(i);
return R;
}
Then it's a two-liner:
range(x, x+width).map(function(x) {
return range(y, y+height).map(function(y) {
return {x:x, y:y}; // anything you'd like
})
})
Result (obtained via JSON.stringify):
[
[ {"x":1,"y":7}, {"x":1,"y":8} ],
[ {"x":2,"y":7}, {"x":2,"y":8} ]
]

Create array from points

I need to format an array from a list of values.
Currently I have the following hard-coded points:
points = [
{x: 20, y: 112},
{x: 23, y: 101},
{x: 51, y: 89},
{x: 63, y: 89},
{x: 84, y: 129},
{x: 64, y: 153},
{x: 45, y: 151},
{x: 38, y: 140},
{x: 28, y: 150},
{x: 10, y: 144},
{x: 0, y: 130},
{x: 10, y: 114}
];
The resulting array is to be passed to a function.
I now need to pass another array to the same function but I cannot hard-coded them as above. Instead, I am using a JS framework to grab the points dynamically using, for example, $(#container).get('points');
How do I convert the new array so that it is formatted similarly to the x: | y: structure as above?
points="20,112, 23,101, 51,89, 63,89, 84,129, 64,153, 45,151, 38,140, 28,150, 10,144, 0,130, 10,114"
I found the following in MooTools:
Array.each([1, 2, 3], function(number, index)
{
alert('x:' + number + ', y: ' + index);
});
...but somehow that doesn't seem like the correct way to do this.
Can I get a little guidance please?
You can split [docs] the string and iterate over it, taking two values in each iteration:
var parts = points.split(',');
var pointsArray = [];
for(var i = 0, l = parts.length; i < l; i += 2) {
pointsArray.push({x: +parts[i], y: +parts[i+1]});
}
You should actualy split it twice:
var points = "12,12, 1,2, 3,4"
var arr = points.split(', ');
for(var i = 0; i< arr.length; i++)
{
var point = arr[i].split(',');
document.write(point+ ' <br />' );
arr[i] = {x:point[0], y:point[1]};
document.write(' '+arr[i].x + ' ' + arr[i].y + ' <br/>');
}
Apart from that, just like the other comment.

Categories