How to move a DOM element inside a closure - javascript

I'm trying to use a closure as per this answer to move a DOM element incrementally. Eventually I want to dynamically create many of these DOM elements that should be moved independently, so that's why I'm trying to define the variable within the function.
The example from the answer given is
var lightning = new Array();
lightning.push($(".lightning"));
var l0 = -210;
function thunkAdd(){
var lThis = l0;
console.log('set lThis to '+ lThis);
return function inc() {
console.log('lThis = '+ lThis);
return lThis ++;
}
lightning[0].css("left", lThis);
console.log('lightning[0] left = '+lightning[0].css("left"));
}
var incrementInterval = setInterval(thunkAdd(), 33);
first example on codepen. Looking in the console, it seems that for some reason, console.log('lightning[0] left = '+lightning[0].css("left")); is not running.
A different approach I tried is:
var lightning = new Array();
lightning.push($(".lightning"));
var l0 = -210;
var lThis = l0;
function thunkAdd(){
lThis ++;
console.log('lThis = '+ lThis);
lightning[0].css("left", lThis);
console.log('lightning[0] left = '+lightning[0].css("left"));
}
var incrementInterval = setInterval(thunkAdd, 33);
second example on codepen. That will move the image across the screen, but ideally (because the next step is to create and move many of these images dynamically) I want to define the variable lThis inside the thunkAddfunction the first time it runs and then if it is defined, increment it and use the lThis variable to move the image. How can I do this?

As already mentioned in comments code after return will not be executed, so you never change the css left property of the element, but just increment variable.
You need to move the lightning[0].css("left", lThis); inside closure that's being executed with interval.
See updated example
You may also consider using jquery's animate instead of this (you can also do this with many elements or in loop)
$(".lightning").animate({left: '2000px'}, 10000, 'linear');

Related

JS getting variable inside the function

I have a function to do something on onClick event.
function onDocumentMouseUp( event ) {
createsprite(clickedposition);
}
createsprite(clickedposition) {
var sprite = JSONSPITE // this add correctly sprite to my scene;
//// here timer function 1 sec
setTimeout(function(){ remove(sprite); }, 1000);
}
Everything works correctly.
But when i click more than once, i have still my variable sprite as a last added one. (yes i know that is correct). And logically remove() have affected only that last one.
What is the correct solution to handle work with unknown amount of same variables.
Purpose it too remove each sprite after one second after click one by one in the order of creation.
How can i reach only one variable in the same function, even when there are more variables with the same name on each function init.
You have defined sprite as a global variable. It will exist not only in that function, but is created on the window object.
If you want to remove only the sprite you created in that function, you should make it a variable
var sprite = JSONSPITE; // typo?
This way, the scope of the sprite variable is just that function.
EDIT: OP has changed their question, making the previous explanation seem obsolete
If you want to create multiple sprites, and store them somewhere so you can access them before they are deleted, you might want to create an array of sprites.
sprites = [];
function onDocumentMouseUp( event ) {
createsprite(clickedposition);
}
function createsprite(clickedposition) {
sprites.push(JSONSPITE); // Adds the current sprite to the end of the array
setTimeout(function(){
var firstSprite = sprites.shift(); // Takes the first sprite out of the array
remove(firstSprite);
}, 1000);
}

Javascript setInterval: multiple intervals

I'm playing with animations. I'm trying to have two circles move a specified distance and then stop. The issue I'm having is with multiple intervals. I assign each interval to a unique variable, and clearInterval that variable, but the first interval continues on.
Here is the code:
function moveRight(player){
if(player==1){
currentLoc = p1Loc[0]; //global var with player's current location
intervalR1 = setInterval(function(){
p1Loc[0]+=1;
draw(); //function that redraws the canvas
if(p1Loc[0]>currentLoc+wu){ //wu is a single "width-unit"
clearInterval(intervalR1);
}
},5)
}
else{
currentLoc = p2Loc[0];
intervalR2 = setInterval(function(){
p2Loc[0]+=1;
draw();
if(p2Loc[0]>currentLoc+wu){
clearInterval(intervalR2);
}
},5)
}
}
Then, let's say I give the following instructions within a while loop:
instructions = ["moveRight(1)", "moveRight(2)"];
for(instruction in instructions){
eval(instructions[instruction]); //I know, eval(), ugh. Is just temporary
}
What ends up happening is, both players start moving right, but player 2 stops after a single wu, or width-unit while player one keeps going. If i change the instructions to only "moveRight(1);", then player one moves a single wu and stops.
What's going on here?
This is just a guess, since is a bit hard to tell what is going on with only part of the code, but could it be that you are assigning currnetLoc twice, and thus the first player's location will always be always be compared against p2Loc[0]?
So when you call moveRight(1) it sets currentLoc to p1Loc[0] then proceeds. Then immediately after you call moveRight(2) which sets currentLoc to p2Loc[0]. So now the comparison for the interval for player 1 is no longer p1Loc[0]>currentLoc+wu, but rather p2Loc[0]>currentLoc+wu. Depending on the value of p2Loc this may always be false.
This line:
currentLoc = p1Loc[0]; //global var with player's current location
is kinda scary. Don't use a global for this, especially when you re-assign its value on the second entry to the function:
currentLoc = p2Loc[0];
That's likely your problem.
If you need to keep track of a single player's position, create a player object and keep track of it in there, something like:
function Player(number) {
this.number = number
this.position = 0 //some default initial position
}
and pass this into moveRight()

The logic of multiple travelling animations in javascript

I have an image of a bug. I want to make 5 copies of that image fly in from the side of the screen and bounce around the screen and bounce off the sides. I want them to all have different starting positions and different directions.
so I made some a global variables
var flyVar;
var flySpeed = 5;
var widthMax = 0;
var heightMax = 0;
var xPosition = 0;
var yPosition = 0;
var xDirection = "";
var yDirection = "";
var bugFly;
var count = 1;
var bug = "bug";
I have a function called setBugs() that I use to set the value of widthMax and heightMax depending on the size of the users screen.
I have a bugStartingPlace function to set the initial starting place for each bug. I won't post the whole function but it does the same for "bug1" through "bug5", giving them different values.
function bugStartingPlace(bugName) {
//Accepts string as argument and sets the starting position and starting direction of each bug.
if (bugName == "bug1") {
xPosition = 0;
yPosition = 100;
xDirection = "right";
yDirection = "up";
}
}
I have a function called flyBug() that does the animation and sets the position of the image. It consists of a bunch of statements like this. I know it works because I can make it work with 1 bug. The problem is doing it with 5 bugs.
function flyBug() {
if (xDirection == "right" && xPosition > (widthMax - document.getElementById("bugImage").width - flySpeed))
xDirection = "left";
<!--More flow control statements are here-->
document.getElementById("bug1").style.left = xPosition + "px";
document.getElementById("bug1").style.top = yPosition + "px";
<!-- More statements are here that set the position of the image -->
}
So, I need some way to get the animation going with the body onload() event. One problem is that setInterval does not allow functions that contain parameters. So I can't put multiple statements in the body onload event that pass "bug1" as a parameter to this function, "bug2" as a parameter to this function and so on. That's why I made the global count variable. That way, any time I need to change the name of the bug, I change the name of count and then do
bug = bug + count;
But that adds a lot of complexity. I need the name of the bug for the bugStartingPlace() function, so I need to change the value of count and also change the value of bug before I use that function. Once I use the bugStartingPlace() function, that changes the value of the global variables. Then I need to use flyBug() before I change the value of bug again.
I guess one of the problems is that I'm using global variables for direction and position even though I have multiple bugs. It works fine for one bug but not for multiple bugs.
Can anyone give me tips on how the logic of this program should work?
setInterval allows, like setTimeout, the use of parameters in the function BUT:
setInterval(funcName(param1,param2...), 100);
wont work. Youll get it to work like that:
var func = function () { funcName(param1,param2..); }
setInterval(func, 100);
To understand that part of javascript, read through dougles crockfords explanation of functions, he tells about this very clear and deep. Link to a video of him
EDIT: Sry i understood your question wrong...
The problem why it wont work is, like you figured out the global vars. You could just make bug an object. His actions will then be methods, which can contain a function and so on. If you then initialize a new bug (you can do this a thousand times then), all the vars stay in the object, without conflicting each other. This is a secure way to provide solidness of your code.
You could do it very simple, with nested functions.
Another way would be, to send the name of the bug vie parameter to the, for example, fly function. And the only work in that function with the parameter given to it.

Multiple simultanous animations, simulation of 'animate' easing and one 'step' call for a jQuery collection

Here is the problem, I've got a tree structure of html blocks, global container is of a fixed width(X) and height(Y). When i click one of the blocks on a level, all other blocks shrink to some size, while the clicked one gets enlarged to the leftover space, and the sublevels show up on it's place.
For all the shrinking i'm using default animate function with easing effect, when shrinking 1 level, to avoid enlargement bugs i have to do something like this:
$tabs.not($obj).animate({height:32<<$obj.getVerUp().length+"px"},{duration:300,
step:function() {
$obj.height(function(){
var sum = 0;
$tabs.not($obj).each(function(){
sum += $(this).height();
});
return $obj.getCont().height()-sum+"px";
});
}
});
$tabs are all the tabs of current level, $obj - is the one tab that i want to enlarge
The main problem is:
When i open up a tab that is on a deep level, i have to animate all the tabs of higher levels to shrink a little bit more, thus the $obj X and Y would change, so the current animation has to use new values, but if i call 3 different animations on different levels i'm bound to get a bug, when one of the animations on a deeper level finishes 1 step earlier, while the one on the level above, would enlarge the object by 5-10 more pixels and that space wouldn't be used up.
The second problem is that there has to be about 50 object animating with easing at the same time, which is a little bit overkill.
And the last problem is when i call step callback on animation as shown above, i have a strange feeling that it calls the step separately for each animation of the $tabs collection, while i need 1 step for all the tabs in the list (to avoid unnecessary scripts)
There might be some other way to fix all that, but i have yet to discover all jQuery functions, so from what i see the only way is to simulate easing, and do everything in one single animation.
I don't really want to use setInterval and determining when do i need to clear it plus calculating each of the easing values, if there is a simple way doing it.
Does jQuery has some sort of empty animation easing, e.g.
$().css("height":starth+"px").animate({height:endh},{duration:300,
step:function(fn) {
// all the animation actions here via fn end value
}
});
Thanks in advance.
What I need - is not a completely working solution in code, just some enlightenment in those subjects:
Is there a legal way to call one step function for a collection of animated elements, or, maybe, it does call step once when I use one .animate on collection.
I'd be really appreciated if someone would shed some light over how does jquery handle multiple .animate, would they be used in one global function that works on .setInterval? or would they be having massive number of those .setIntervals that are equivalent to setTimeout (which most browsers can't handle in large amounts);
Is there a way to simulate 'animate' easing, some function name maybe, or a special trick to achieve that (the only thing I see is a hidden element or 'window' property to change maybe)
Or some directed pushes with functions I should study, that could help me achieve my goals
Guess i pretty much found the answer to my questions:
http://james.padolsey.com/javascript/fun-with-jquerys-animate/
Here's the empty animation from the link above with 1 step function with desired values, going to post the result later on if it all works out.
var from = {property: 0};
var to = {property: 100};
jQuery(from).animate(to, {
duration: 100,
step: function() {
console.log( 'Currently # ' + this.property );
}
});
Yes it all worked great, no desynch, and a good speed, since only 1 animate, found making one universal function for the animation - waste of resourses, so it is pretty specific, but still, here it is:
animate: function($obj) {
var T = this;
...
T.arr = new Array();
// gathering the array
$obj.each(function(i){
var size;
T.arr[i] = {obj:$(this), rest:$(this).getSibl(), cont:$(this).getCont()}
if($(this).hasClass("vert"))
{
size = "height";
T.arr[i].to = yto;
}
else
{
size = "width";
T.arr[i].to = xto;
T.arr[i].children = $(this).getChld();
}
T.arr[i].rest.each(function(){
$(this).attr("from",$(this)[size]());
});
});
// animating prop
jQuery({prop:0}).animate({prop:1}, {
duration: 300,
step: function() {
var i;
var P = this;
var newval;
var sum;
var size;
for(i = 0; i < T.arr.length; i++)
{
size = T.arr[i].obj.hasClass("vert") ? "height":"width";
sum = 0;
T.arr[i].rest.each(function(){
// new value of width/height, determined by the animation percentage
newval = parseInt($(this).attr("from")) + (T.arr[i].to-$(this).attr("from"))*P.prop;
$(this)[size](newval);
sum += newval;
});
T.arr[i].obj[size](T.arr[i].cont[size]()-sum);
}
}
});
},

recursive function and setTimeout() problem

I have a script that draws a bunch of lines on canvas, but it's pretty intense so while rendering freezes browser for a few seconds. I added setTimeout() so that the browser wouldn't freeze and it effectively messed up my script. It's difficult to explain how, so I have two examples online:
Without setTimeout() : http://www.modwebsolutions.com/test1
With setTimeout() : http://www.modwebsolutions.com/test2
Note, that I only change a single line in the whole script, that is line 69:
without setTimeout(): vLoop();
with setTimeout(): setTimeout(vLoop,1);
The problem here, as hinted at by others, is that you are drawing the lines a quadrant at a time. As soon as the SetTimeout method is called and the first vLoop returns, the code carries on running into the next drawVertical which changes all the global variables and so on.
What you need to do is synchronise how you're calling vLoop and how you are changing the globals.
This is basically the solution:
Replace ...
drawVertical(c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2+50,y);
drawVertical(c,step,stepInt,bigStep,xStart,xEnd,y/2+50,y);
drawVertical(c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2-50,0);
drawVertical(c,step,stepInt,bigStep,xStart,xEnd,y/2-50,0);
... with ...
var q = new Array();
q[0] = [c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2+50,y];
q[1] = [c,step,stepInt,bigStep,xStart,xEnd,y/2+50,y];
q[2] = [c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2-50,0];
q[3] = [c,step,stepInt,bigStep,xStart,xEnd,y/2-50,0];
drawQuadrant(q, 0);
Replace your drawVertical function with ...
function drawQuadrant(q, i)
{
var r = q[i];
c__ = r[0];
step__ = r[1];
stepInt__ = r[2];
bigStep__ = r[3];
xStart__ = r[4];
xEnd__ = r[5];
yStart__ = r[6];
yEnd__ = r[7];
vLoop(q,i);
}
change the vLoop function prototype to look like this ...
function vLoop(q,i)
and finally replace your recursive vLoop call (from within vLoop) with ...
if ((xStart__ > 0) && (xStart__ < window.innerWidth))
{
setTimeout( function(){vLoop(q,i)}, 1 );
}
else if (++i < 4)
{
setTimeout( function(){drawQuadrant(q,i)}, 1 );
}
The last block is where it ensures that the quadrants are not stepping over each other.
drawVertical(c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2+50,y);
drawVertical(c,step,stepInt,bigStep,xStart,xEnd,y/2+50,y);
drawVertical(c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2-50,0);
drawVertical(c,step,stepInt,bigStep,xStart,xEnd,y/2-50,0);
Your calling 4 recursive functions of vLoop at once. The problem here is that setTimeout is non-blocking where as recursion is blocking. So basically you now have all 4 of these drawVertical functions running in parallel rather then in sequence.
The other problem is that all 4 refer and mess with global state and your entire program breaks.
What's happening is what setTimeout() delays all that execution until later. Unfortunately, by that point your global variables have all moved to their ending positions from the initial loop, since it completed before the first line was drawn.
If you moved your timeout further up (so the shared variables you're using aren't affected until draw time) you could achieve what you're after, for example:
setTimeout(function() {
drawVertical(c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2+50,y);
drawVertical(c,step,stepInt,bigStep,xStart,xEnd,y/2+50,y);
drawVertical(c,-1*step,-1*stepInt,-1*bigStep,xStart,xEnd,y/2-50,0);
drawVertical(c,step,stepInt,bigStep,xStart,xEnd,y/2-50,0);
});
Then this would work (but it's dangerous, order isn't absolutely garunteed!)
You can see a working example here.

Categories

Resources