I have code that creates an object array in a layer as shown below:
var labels = layer.get('Label');
var labelCount = labelLeft.length;
var tweens = [];
var tweenCounter = 1;
var duration=5;
for(var i=0; i<labelCount; i++)
{
var tween = new Kinetic.Tween({
node: labelLeft[i],
duration: animspeed[i],
x: 0,
onFinish: function() {
if (tweenCounter !== labelCount) { //Prevent an undefined tween from being played at the end
tweens[tweenCounter].play();
tweenCounter++;
}
}
});
tweens.push(tween);
}
tweens[0].play();
The problem is that I want to hide the object once done scrolling to left using onFinish. I tried using labelLeft[i].hide()
onFinish: function() {
labelLeft[i].hide();
if (tweenCounter !== labelCount) { //Prevent an undefined tween from being played at the end
tweens[tweenCounter].play();
tweenCounter++;
}
}
But this triggers TypeError: labelLeft[i] is undefined
Any ideas? Please help. Thanks
It seems as closure problem. Could you try this, I am not sure if it will work but anyway:
for(var i=0; i<labelCount; i++)
{
var label = labelLeft[i];
var tween = new Kinetic.Tween({
node: labelLeft[i],
duration: animspeed[i],
x: 0,
onFinish: function(l) {
return function()
hide(l);
}(label)
});
tweens.push(tween);
}
function hide(label) {
labelLeft[label].hide();
if (tweenCounter !== labelCount) { //Prevent an undefined tween from being played at the end
tweens[tweenCounter].play();
tweenCounter++;
}
}
Try to debug your code and check of i is defined.
I think its not because you are in a for loop and execute the code when the loop has finished. You easily can solve this problem with a anonymous function to induce a scope.
for(var i=0; i<labelCount; i++)
{
(function(i){
//put code here
}(i))
}
Related
I have 3 clickable objects. When one is clicked, this becomes the 'selected unit'.
I am trying to create some generic actions for the units such as moving to a point.
In my create function I initialize the units, when a unit is clicked on - this is supposed to become the 'selected unit' so that my movement and direction function applies to the this unit. However, the script is not able to recognize which unit intend for example I get this error:
Uncaught TypeError: Cannot read property 'velocity' of undefined.
Is there a way to use a variable to indicate selected users and pass that to the functions?
window.onload = function() {
var block_count = 0;
var block = '';
var selected_unit = '';
var unit_clicked = 0;
var tank1 = null;
var game = new Phaser.Game(800, 600, Phaser.AUTO, '', { preload: preload, create: create, update: update, render: render});
function preload () {
game.load.image('block', 'block.png');
game.load.image('tank1', 'tank.png');
game.load.image('baddie', 'tank.png');
game.load.image('mouse_btn', 'block.png');
game.input.mouse.capture = true;
}
function create () {
game.physics.startSystem(Phaser.Physics.ARCADE);
mouse_btn = game.add.sprite(30,30, 'mouse_btn');
mouse_btn.anchor.setTo(0.5, 0.5);
//T1
tank1 = game.add.sprite(30,30, 'tank1');
initialise_player(tank1);
game.physics.enable(tank1, Phaser.Physics.ARCADE);
//T2
tank2 = game.add.sprite(30,60, 'tank1');
initialise_player(tank2);
game.physics.enable(tank2, Phaser.Physics.ARCADE);
game.world.setBounds(0, 0, 2000, 2000);
game.camera.follow(tank1);
}
function update () {
if(selected_unit == '') {
mouse_btn.x = game.input.mousePointer.worldX
mouse_btn.y = game.input.mousePointer.worldY
}
if(game.input.activePointer.leftButton.isDown && block_count == 0 && unit_clicked == 0) {
game.input.activePointer.leftButton.stop(event);
block_count =1;
block = game.add.sprite(game.input.mousePointer.worldX, game.input.mousePointer.worldY, 'block');
game.physics.enable(block, Phaser.Physics.ARCADE);
block.anchor.setTo(0.5, 0.5)
lookAtObject(selected_unit, block, 0.005);
}
if(block.alive){
game.physics.arcade.moveToObject(selected_unit, block, 260, 0)
} else {
console.log(selected_unit)
selected_unit.body.velocity.x = 0;
selected_unit.body.velocity.y = 0;
}
if(game.physics.arcade.collide(selected_unit, block)) {
block_count--;
block.kill();
}
}
function render(){
//console.log(game.physics.arcade.collide(tank1, block))
}
function lookAtObject(obj, target, rotspeed){
var angle = Math.atan2(block.y - tank1.y, block.x - tank1.body.x);
tank1.rotation = angle + game.math.degToRad(90);
}
function initialise_player(tank1){
tank1.anchor.setTo(0.5, 0.5);
tank1.inputEnabled = true;
tank1.input.useHandCursor = true;
tank1.events.onInputDown.add(t1_clicked,this);
tank1.events.onInputOver.add(t1_over, this)
tank1.events.onInputOut.add(t1_out, this)
}
function t1_clicked() {
selected_unit = tank1;
}
function t1_over() {
unit_clicked = 1
}
function t1_out () {
unit_clicked = 0
}
};
The error you're getting on initial load is because in update you're making an assumption that selected_unit exists and has a body.
Update your third if to make sure selected_unit is defined.
if (selected_unit !== '') {
selected_unit.body.velocity.x = 0;
selected_unit.body.velocity.y = 0;
}
However, a better option would be to put this a few lines down, where you kill the block instead.
if(game.physics.arcade.collide(selected_unit, block)) {
selected_unit.body.velocity.x = 0;
selected_unit.body.velocity.y = 0;
block_count--;
block.kill();
}
if (block.alive); moveToObject is also expecting selected_unit to exist and have a body, which may not be the case; wrap it with a check.
if (selected_unit !== '') {
game.physics.arcade.moveToObject(selected_unit, block, 260, 0)
}
That now allows tank1 to rotate to look at the item you just placed, but it doesn't move it until it or tank2 have been clicked on.
This also points out that there are a number of tweaks you'll want to make to your code in general, since you're ignoring arguments that are being passed in. For example, t1_clicked isn't using the sprite that's been clicked on, but is instead just hard-coding tank1. lookAtObject isn't using obj or target, but again has values hard-coded in.
One other thing you may want to change is the following:
if(selected_unit == '') {
mouse_btn.x = game.input.mousePointer.worldX
mouse_btn.y = game.input.mousePointer.worldY
}
If you make that the following, you won't end up with an extra sprite hanging about on the screen.
if (block_count === 0) {
mouse_btn.x = game.input.mousePointer.worldX;
mouse_btn.y = game.input.mousePointer.worldY;
}
I have stripped back my Fiddle to show my issue.
I have a Rectagle that I am looping so it gives a different name with multiple instances. I can add and remove the rectangles fine. My issue is that I am replacing this rectangle with an image and I can get it to loop ok, but it will only remove 1 image and not them all. I think I am doing something wrong with the name, but i can not see the wood through the trees.
JSFiddle
var canvas = new fabric.Canvas('c', { selection: false });
var land = [];
var turret = [];
var wh=[];
var op=[];
//////////////////////////////////////////////////////////////////
// Outpost Level Dropmenu Select
$(document).ready(function () {$('#method').change(
function () {
var method = $('option:selected').val();
if (this.value == "0") {
for (var i=0; i<96; i++){
canvas.remove(land[i]);
canvas.remove(turret[i]);
canvas.remove(wh[i]);
canvas.remove(op[i]);
};
} else if (this.value == "1") {
//Clear All
for (var i=0; i<96; i++){
canvas.remove(land[i]);
canvas.remove(turret[i]);
canvas.remove(wh[i]);
};
//Add Buildings
for (var i=0; i<40; i++){
land[i] = new fabric.Rect({
fill: 'green',left: 25,top: 25,width: 25,height: 25,
perPixelTargetFind: true,hasBorders: false,hasControls:false, hasRotatingPoint: false,
});
canvas.add(land[i])
};
for (var i=0; i<6; i++){
turret[i] = new fabric.Rect({
fill: 'brown',left: 75,top: 25,width: 15,height: 15,
perPixelTargetFind: true,hasBorders: false,hasControls: false,hasRotatingPoint: false,
});
canvas.add(turret[i])
};
for (var i=0; i<3; i++){
fabric.Image.fromURL('http://www.ahoymearty.co.uk/baseplanner2/images/buildings/warehouse.png', function(myImgwh) {wh[i] = myImgwh.set({ left: 25, top: 75 ,width:25,height:25, hasControls: false, hasRoatatingPoint: false,stroke: 'blue',strokeWidth: 1,
});
canvas.add(wh[i]);
});
};
}
});
});
All 3 of your images are added async with a reference to i and the reference to i is 3 when all images are added. if you look at your 'wh' array you have a bunch of undefineds and then only 1 object added to the array. that is why you can only delete one object.
this JSFiddle just gets a new id in the callback which sets the array up correctly and then they can be deleted since you will now have all the proper references.
if it's acceptable to clear the whole canvas then this you can use the context -fiddle here https://fiddle.jshell.net/2xozu3wk/ as discussed in this query How to undraw, hide, remove, or delete an image from an html canvas? I think all but one of your images is being committed to the canvas and images paint pixels in a manner that's not so easy to clean up. Alternatively you can blank the canvas and reinstate elements you wish to retain but be prepared for a flicker?
var canvas = new fabric.Canvas('c', { selection: false });
var context = canvas.getContext('2d');
.
.
.
if (this.value == "0") {
context.clearRect(0, 0, canvas.width, canvas.height);
}
don't know if that helps
I have the following function
function draw(i, arrayIdColetor, callback) {
var query = new $kinvey.Query();
query.equalTo('idColetor', arrayIdColetor[i]);
var promise = $kinvey.DataStore.find('myDatabase', query);
var latLong = promise.then(function(response) {
coordenadas = [];
for(var j = response.length - 1; j >= 0 ; j--) {
coordenadas.push( {lat: response[j].lat, lng: response[j].lng});
}
return coordenadas;
});
latLong.then(function(coordenadas) {
$kinvey.poly[i] = new google.maps.Polyline({ path: coordenadas, ... });
$kinvey.poly[i].setMap($kinvey.map); });
callback();
}
This function is called by the following function:
function callFor(j, arrayIdColetor) {
if (j < arrayIdColetor.length){
draw(j, arrayIdColetor, function() {
callFor(j + 1, arrayIdColetor)
});
}
}
The function callFor take a couple of seconds to be executed and I'd like that all the interface buttons be disabled while callFor functions is running. What should I do to solve it?
One more question, I have another function that I'd like that runs always after the callFor function is finished.
Thanks for any help.
Maybe you could try this? I didn't really get why you were using recursion as you're not doing anything that would suggest its use, and it's only complicating your wished behavior.
function callFor(j, arrayIdColetor) {
//disable buttons
for (i = j; i < arrayIdColetor; i++) {
draw(i, arrayIdColetor, function(){});
}
//enable buttons
}
I understand that in order to remove an interval you need a stored reference to it, so I figured I'll store function returns in a global array.
But why when I click on a cell the intervals keep going and only one stops (I saw first and last cell to stop flashing)?
What I wanted it to do is append a continuous fadeTo on all cells of a table row (excluding first one), and a listener that on clicking any of these cells would stop animations on all of them.
Here's my best effort so far (jsfiddle):
var intervals;
var target = $('#table');
var qty = target.find('td:contains(Price)');
var row = qty.closest('tr');
arr = new Array();
row.find('td').each( function() {
if ($(this).text() !== "Price" ) {
intervals = new Array();
addAnimation($(this));
}
});
function addAnimation(cell) {
var intv = setInterval(function() {
cell.fadeTo("slow", 0.3);
cell.fadeTo("slow", 1);
}, 1000);
intervals.push(intv);
cell.click(function() {
for (var i = 0; i < intervals.length; intervals++) {
window.clearInterval(intervals[i]);
}
});
}
You are instantiating the intervals array multiple times and incrementing the wrong parameter in the for loop:
var intervals = [],
target = $('#table'),
qty = target.find('td:contains(Price)'),
row = qty.closest('tr');
row.find('td').each( function() {
if ($(this).text() !== "Price" ) {
addAnimation($(this));
}
});
function addAnimation(cell) {
var intv = setInterval(function() {
cell.fadeTo("slow", 0.3);
cell.fadeTo("slow", 1);
}, 1000);
intervals.push(intv);
cell.click(function() {
for (var i = 0; i < intervals.length; i++) {
window.clearInterval(intervals[i]);
}
$(this).stop();
});
}
See: fiddle
Your other problem is here:
var intervals;
...
if ($(this).text() !== "Price" ) {
intervals = new Array();
addAnimation($(this));
That creates a new array each time. You should be initialising intervals when you declare it and delete the line creating a new array in the if block:
var intervals = [];
...
if ($(this).text() !== "Price" ) {
addAnimation($(this));
}
However, you may wish to run this more than once, so you should clear out the array when you clear the intervals, something like:
function addAnimation(cell) {
var intv = setInterval(function() {
cell.fadeTo("slow", 0.3);
cell.fadeTo("slow", 1);
}, 1000);
intervals.push(intv);
cell.click(function() {
for (var i = 0; i < intervals.length; intervals++) {
window.clearInterval(intervals[i]);
}
// reset the array
intervals = [];
});
}
or replace the for loop with something like:
while (intervals.length) {
window.clearInterval(intervals.pop());
}
which stops the intervals and clears the array in one go. :-)
I'm trying to create multiple instances of a slider on a page. Each slider should know which slide it’s currently viewing. It seems that when I update the slide property, I change it for the class, and not the instance. This suggests to me that I'm not instantiating properly in my public init() function. Where am I going wrong?
var MySlider = (function() {
'use strict';
var animating = 0,
slides = 0, // total slides
slider = null,
slide = 0, // current slide
left = 0;
function slideNext(e) {
if ((slide === slides - 1) || animating) return;
var slider = e.target.parentNode.children[0],
x = parseFloat(slider.style.left);
animate(slider, "left", "px", x, x - 960, 800);
slide++;
}
return {
init: function() {
var sliders = document.querySelectorAll('.my-slider'),
l = sliders.length;
for (var i = 0; i < l; i++) {
sliders[i] = MySlider; // I don't think this is correct.
slider = sliders[i];
buildSlider(slider);
}
}
}
})();
Based on your comment, I think this is more like what you want:
MySlider = (function () {
Slider = function (e) {
this.e = e;
// other per element/per slider specific stuff
}
...
var sliders; // define this out here so we know its local to the module not init
return {
init: function () {
sliders = document.querySelectorAll('.my-slider');
var l = sliders.length;
for (var i = 0; i < l; i++) {
sliders[i] = new Slider(sliders[i]); //except I'd use a different array
slider = sliders[i];
buildSlider(slider);
}
}
})();
This way, you are associating each element with it's own element specific data but you have a containing module within which you can operate on your collection of modules.
It seems that when I update the slide property, I change it for the class, and not the instance.
You are correct. That code is run only when the MySlider class is defined. If you want an instance variable, you need to declare it inside the returned object, ie, part of your return block:
var MySlider = (function(param) {
return {
slider: param,
init: function() {
var sliders = document.querySelectorAll('.my-slider'),
l = sliders.length;
for (var i = 0; i < l; i++) {
sliders[i] = MySlider; // I don't think this is correct.
slider = sliders[i];
buildSlider(slider);
}
}
});