Snap SVG animation - Infinite Loop Freezing - javascript

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.

Related

How to make elements reappear after animation with setTimeout()

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)

JS AnimationEnd Loop, would like to loop continually WITHOUT using; setInterval/setTimout

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.

Moving an image up the screen using do and while loops

I am learning JavaScript and have been developing a simple game that is essentially a balloon that you can move around on the screen. I managed to do keypresses etc with a lot of help and the balloon moved about just perfectly.
I now want to simulate gravity, by having the balloon move one pixel down the screen if the balloon image was above a value, i tried to do this with the following do while statement:
var balloon = document.getElementById("balloon");
var bottom = parseInt (balloon.style.bottom, 10);
do {
balloon.style.bottom = bottom + 50 + 'px';
}
while (bottom = bottom > 600) // Gravity
What I want this to do, is to check the code is working by making the balloon move up the page 1 pixel if the bottom value is less than 600.
I have stripped out all the code I used to make the balloon move.
If I could just see the balloon move slowly up the page I would be very happy, because then at least I know I can just switch the values round when I've added the movement back in.
The other answers address the issue of attempting an animation with an explicit loop. As they have pointed out, you should use timers.
Because it seemed like fun, I made you a simple example of how to use a timer to animate a balloon falling:
http://jsfiddle.net/dmuu9w97/
The key code is the following:
// Make balloon fall 1px every 10ms
setInterval(function() {
var bottom = getBalloonBottom();
if (bottom > 0)
balloon.style.bottom = bottom - 1 + "px";
}, 10);
For your while loop condition should be (bottom>600) . No need for '='
You are loading the variable bottom outside the loop. It will never change. If it is 610 at the start of the loop it will remain 610 because it is assigned only in line 2 of your code
While loop should probably be ....bottom = (bottom - 1) + 'px';
If you write a while loop like this, it will execute 10 times immediately and your baloon will be always stuck in 600
To solve 'stuck at 600' problem, you should use a timer:
Think about "how fast should the balloon fall". Then you can come up with some number like "5 pixels in 100 milliseconds".
Then write a function... call that function on a timer.
Check the setTimeout function here...
setTimeout method
It's not impossible to do with a do loop but I think you ought to abandon this explicit loop in favor of javascript's timer/timeline. Look into how to use window.setTimeout() where the body of your do loop becomes the body of the callback function AND a trailing call to window.setTimeout() passing the callback again with a delay of 1000/your-chosen-framerate milliseconds. Then you can also process keypress events in their own handlers for intentional movement.
If you use an explicit loop, you'll only get gravity because the loop should never end (just as gravity never stops pulling) and therefore the browser will never have a chance to call the keypress event handler.
Your timeout callback runs once, queues itself again, and terminates. That gives control back to the browser's javascript engine to process events or, if nothing else, run the callback function again after the requested delay.
requestAnimationFrame may be more appropriate than setTimeout in modern JS implementations. It usually leads to a smoother result for animations.

js setTimeout synchronize

In fact, when I use
setTimeout(a(),60);
setTimeout(a(),120);
setTimeout(a(),180);
setTimeout(a(),240);
It is supposed to be 60ms gap between calling's of a functions.
But it isnt, especially when it is fired during page loading or animating elements. In fact that gap gets even 2x longer when browser 'has hard work to do'. In some cases it can be visible easly.
The point of question is - is there any other way to synchronize events or functions in time in javascript?
The timing in setTimeout(a(),60) in simple terms translates to I will run this function no earlier than 60ms, but if I get busy it could be later than that.
Therefore, setTimeout does not promise when the execution will take place, only that it will take place sometime after the given time in milliseconds.
So to answer your question, no there is no way to guarantee execution time with setTimeout but you can load your script after the DOM has loaded so that JavaScript is not busy anymore loading other things. In jQuery you can use the $(document).ready() function for that purpose.
Read this article by John Resig for more information about timing in JavaScript: http://ejohn.org/blog/how-javascript-timers-work/
Try this:
setTimeout(a,60);
setTimeout(a,120);
setTimeout(a,180);
setTimeout(a,240);
Note that the function doesn't have the ()s.
In your particular case, setInterval() might work:
var count = 0, interval = setInterval(function() {
count += 1;
if (count > 4) {
clearInterval(interval);
} else {
a();
}
}, 60);
Note that jQuery has a built-in animation feature that uses the different, better approach of simply treating an animation as a function of time and frequently checking the clock, so an unexpected delay would simply make the animation a bit less smooth.

jQuery delay vs. setTimeout for jackpot "spinner" image swapping

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.

Categories