In the below code I am trying to loop three functions that only fire once the previous function is complete, with the last function then calling the first to start the process all over again. Using setInterval/setTimout are not going to be good answers for this because of RequestAnimationFrame taking their place as a cleaner way of doing things but I dont know how to apply RequestAnimationFrame to this code. Also the question of why the third function does not call the first wouldn't be answered by using those two methods as well.
<body onload="runOne()">
function runOne(){
var x = document.getElementById("rightBox");
document.getElementById("rightBox").style.animation = "scrollTextTwo 10s";
x.addEventListener("animationend",runTwo);
};
function runTwo(){
var x = document.getElementById("rightBoxTwo");
document.getElementById("rightBoxTwo").style.animation = "scrollTextTwo 10s";
x.addEventListener("animationend",runThree);
};
function runThree(){
var x = document.getElementById("rightBoxThree");
document.getElementById("rightBoxThree").style.animation =
"scrollTextTwo 10s";
x.addEventListener("animationend",runOne);
};
The above code works only once, it will play/animate all three functions but then stops after "runThree()" is complete. I would like to know how "runThree()" can call "runOne()" once run three is completed with its animation?
So, I think you have several options: What could work is that you reset the the animation of rightBox in function runTwo with animation: none. If you assign scrollTextTwo 10s back to the rightBox it should start again. Equivalent for the other ones.
See the following Codepen, where I implemented an endless CSS animation using JavaScript.
Alternatively it's also possible to do it without JavaScript: You can use animation-delay, infinite repeating and some other tricks to create really complex animation timelines, maybe also take a look at the following question.
Related
So it's probably some mis-understanding on the best way to use the setTimeout method provided by javascript but im having trouble implementing it in a way that makes sense.
Essentially I have an Array with numbers between 1-4 and each number corresponds to a button getting let up.
for(let i = 0;i < arr.length;i++){
view.renderPane(arr[i]) //All this does is set the .css
view.renderPane is pretty simple:(I have a separate function that clears(sets opacity back to .5) it, but if possible i'd like to just put that in here.
renderPane(pane){
$("."+pane).css("opacity", "1");
console.log("Activating Pane "+ pane)
}
So I tried setting up a timeout thinking I could call the renderPane within the timeout, but all it did was set up a bunch of timeouts that basically fired off after X seconds (or milliseconds). Is there a way I can call the renderPane(pane) function every 1 second (to set up a delay) inside this for loop? or will I need to set up something else?
No need to use a loop, just create a function which continuously schedules itself with setTimeout until it's done — in this case, it removes an item from the array in each call and stops when the array is empty:
(function callee() {
view.renderPane(arr.shift());
if (arr.length)
setTimeout(callee, 1000);
})();
Example: https://jsfiddle.net/2fwht35d/
There are many other ways to implement this behaviour, but this should give you a good starting point.
I am making a simple JS program and am having some trouble. You can view it here
http://codepen.io/TheAndersMan/pen/mOGVEy?editors=0010
Enter in your name and press enter, it will animate your name, but the letters disappear after the animation, which is understandable. what I don't understand is why my setTimeout isn't working and making them re-appear.
So here is the basic problem:
var timeOut = (a / 2 + 1) * 1000;
document.querySelector(".spanWrap").style.width = char.length * 60 + "px";
setTimeout(function() {
thang.style.opacity = "1"
thang.style.marginTop = "0";
}, timeOut);
So hopefully that is all the info you will need, if not reference my pen, this is all in a for loop and if you see undefined variables here, they are defined in my pen.
So if someone can tell me what I did wrong that would be great.
Thanks in advance!
You have the infamous closure bug.
I noticed that you are transpiring using Babel. Using let instead of var for your variables local to your loop should fix the issue. Notice that in your broken CodePen, the last letter stays while the rest disappear. That is because your thang is always equal to the last letter by the time the timeout handlers execute (the loop has concluded long before).
See http://codepen.io/anon/pen/ObaVyb.
Also, a better idea might be to take a look at animation-fill-mode: forwards, which allows you to retain styles after animations have been run.
Finally, for those of you not using ES6, this code will allow you to achieve the same functionality without creating another wrapper function. (Essentially, setTimeout allows you to pass arguments to your callback when you register each handler.)
setTimeout(function (thang) {
thang.style.opacity = "1"
thang.style.marginTop = "0";
}, timeOut, thang);
The problem is, that you have several timeouts in for loop, that needs references to thang variables, but when your timeouts will be executed thang variable will be equal to the last thang in the cycle, so all the timeout would have the same reference. Hope it's clear.
So, to fix that, you need to bind your timeouts with thangs variables, one by one.
For example, you can do it with closures:
(function(thang) {setTimeout(function() {
thang.style.opacity = "1"
thang.style.marginTop = "0";
}, timeOut);})(thang)
I have issues trying to loop this animation:
svg = document.getElementById("shape");
s = Snap(svg);
var l2 = Snap.select('#line2');
animatePath();
function animatePath(){
l2.animate({ d: "M328.2,29.9c-51.6,8.3-65.9-2.5-79.4,23.5c-5.4,10.5-12.8,24.3-21.6,47.7c-14.7,38.6-131.1,9.1-37.8,108.8c92.8,99.6,101.9,43.3,89.2,22.9c-7.1-11.4,40.5-1.6,95.5-3.7c18.3-0.6,37.1-2.8,54.8-7.5c71.7-19.1,12-85.7-47-119.9S398.5,18.6,328.2,29.9z" }, 1000, mina.ease, resetPath);
}
function resetPath(){
l2.animate({ d: "M218.5,85.1c-8,18.9-33.1,25.7-43.6,40.9c-10.8,15.6-9.5,38,38.5,89.3c93.2,99.6,121,58.2,107.9,37.8c-9.8-15.3-7.6-35.7,64.8-53.5c49.3-12,56.4-24.5,36.9-42c-9.5-8.5-24.7-18.3-43.8-29.8c-10.8-6.5-17-13.5-20.3-20.6c-14.5-30.5,25.4-61.6-32.3-51.8C256.6,67.4,252.6,4.4,218.5,85.1z" }, 1000, mina.ease, animatePath);
}
The first loop is fine, but a few seconds later animation starts to freeze.
I guess the problem comes from the callback but after many tries I can't figure it out.
Does it work fine for single animations, but gets messed up for multiple ones ?
I think the problem is that you have lots of the same callbacks firing at the same time, so you end up in an escalating loop.
Just have one callback in each function from one of the animations, rather than one for every animation. So you only call resetPath or animatePath after one animation, and remove the callbacks off the other animations.
Actually yes, it was working perfect with a single animation.
I removed multiple callbacks in each functions, worked just fine.
Thanks a lot for your assistance.
I am working on coding for a situation where I need to construct a function of nested callbacks of an unknown length. It is to create a sequenced animation queue to move an element across an unknown # of positions.
For example, output would look something like this with X 'complete' callbacks nested inside:
$('#element').animate(css, { complete: function () {
$('#element').animate(css, { complete: function () {
// more nested calls inside
}
});
Right now I am generating these functions as a string, and then once completed, feeding it to new Function():
myFunc = new Function(generatedFuncString);
The content is trusted but this still uses eval() which has negative performance implications. I was just wondering if there is another/better way?
edit: The reason I am doing it this way is because I have a very complicated set of animations to perform and am working outside of the jQuery animation queue. If anyone has a better suggestion for how to accomplish a situation like this that would be helpful...
Imagine a baseball diamond with a runner(A) on 1st and a runner(B) on 3rd. In one animation bundle, I want to animate runner A to 3rd (stopping at 2nd in the middle, 2 advances), and runner B to HOME (1 advance).
I have to fire-off the initial advance with 'queue: false' so that runner A and B move to their first base at the same time (runner A to 2nd, runner B to home).
When Runner A is done moving to 2nd, I want to then move him to 3rd (hence constructing a animate() call with nested callbacks pro grammatically to ensure this sequencing is preserved).
The reason I am constructing the function via string is because I know what the inner-most callback is going to be first, and then recursively constructed 1 or more outer-callbacks from there. I couldn't figure out a way to do this by working with functions as objects and keeping all of the references in tact.
Keep in mind this is a simple example. Imagine a situation where the bases are loaded, and I need to animate a grand slam (all 4 runners circle all bases, runner originating at home needs to make 3 stops before running back to home). Etc etc.
Answering the question you ask in your title: You can create functions from strings via eval, new Function, and by inserting a script element with the text you want. But it all comes to the same thing: Firing up the JavaScript parser and creating the function.
But rather than nesting, I think you want chaining. Build a list of the animations in an array, and use the animate callback to call the next animation in the array. Something like:
var animations = [
/* css for first animation */,
/* css for second animation */,
/* etc. */
];
var index = 0;
function runAnimation() {
if (index < animations.length) {
$("#element").animate(animations[index++], runAnimation);
}
}
You'd build up the array dynamically, of course.
gdoron points out in the comments that if all of your animations are on the same element, it can be even simpler:
var animations = [
/* css for first animation */,
/* css for second animation */,
/* etc. */
];
var index = 0;
for (index = 0; index < animations.length; ++index) {
$("#element").animate(animations[index]);
}
...because when you call animate multiple times on the same element, by default the animations queue up (the queue option defaults to true; sadly the docs don't seem to say that). My code example above doesn't rely on the queue, and so in theory each entry in the array could be an object with a property for the selector for the elements to animate and the css to apply. But if it's all one element, you can just use a straight loop.
Scenario:
I want to create a jQuery controllable jackpot "spinner" that will rapidly sequence a number of random images through a div before settling on one, with the delay interval between each equal but changeable. For mockup purposes, I'm simply changing CSS color classes to a box, although in the final I'll use background images.
I thought this would be a no-brainer to do with a loop. I'm sure there's a more efficient way to do this, but guessed the below would work fine. However, I discovered I have no way to control the CSS color swap speed. This whips through the color class changes instantly and just shows the last one. What I'd like is a delay where indicated.
jQuery delay() doesn't seem to work when chained with addClass(), though it works fine with effects. So I tried using window.setTimeout, but as far as I can see, in this context it requires a kludgey function call. The code as written below executes all the function calls after the loop has run. Is this a closure issue? Don't want to use setInterval because these will be limited iterations.
Thanks for any advice!
for (var j= 9; j >= 0; j--) {
$('#box1').attr('class', 'boxes'); // strips all current classes, replaces them with class 'boxes', which has general CSS characteristics
var numRand = Math.floor(Math.random() * 6);
var randomClass = colorArray1[numRand]; // pull random class from an array of six choices
$('#box1').addClass(randomClass);
// Everything above here works fine, would like loop delay here
// Tried using straight-up setTimeout -- doesn't appear to like loops
window.setTimeout(outerFunc, 1000);
};
function outerFunc() {
alert('nobody here but us chickens!');
};
If you want to use .delay() with a method like .addClass(), you can add it to the queue with jQuery's .queue() method.
$('#box1').delay(1000)
.queue(function( nxt ) {
$(this).addClass(randomClass);
nxt(); // allow the queue to continue
});
Otherwise, if I get what you want, you could multiply the 1000 ms for the setTimeout() by the current value of j, so that each time the duration increases.
window.setTimeout(outerFunc, (1000 * j));
setTimeout and setInterval work differently in javascript to the way you want to use them.
Both functions take the function that you pass in and attach them to the window DOM object. Then, after the delay you have passed in has passed, and when there is no other script currently running, they get called from the window object.
To get the functionality you are after, you will need to convert your code so that the jQuery addclass call is inside the function you are passing to setTimeout.
Perhaps recursion would work?
// this code is not tested
var j = 9;
function myFunc() {
// code here
j--;
if(j >= 0) setInterval(myFunc, 1000);
}
I haven't used the queue class in jQuery myself (first I've heard of it, but it sounds cool). That might be the better answer, but this should be a decent alternative if the queue doesn't work as expected.
UPDATE: I just noticed that in your code it looks like you are expecting setTimeout to work like Thread.Sleep in .Net. setTimeout doesn't work that way. It works more like Thread.Start where your code continues on as soon as you call it.