Loop kinetic javascript transitions one after other instead of all at once - javascript

I want to to queue several transitions one after the other in html5 canvas.
Looping the transition function calls all the transitions at once. I dont know if callback will be do this if the iterations are more than 100.
I want to do something like this:--
for(i=0;i<10;i++)
{
move(circle,Math.floor(Math.random()*1000),400);
}
move is my defined function which makes some transitions.its working perfectly fine.
Here, i want the circle to change its postion with every iteration but its changing its position only once.

You could do this:
var i=10;
var interval = window.setInterval(function(){
move(circle,Math.floor(Math.random()*1000), 400);
console.log(i);
if(!--i) {
window.clearInterval(interval);
}
}, 400); // wait 400 msecs between calls
Or, if your move function was willing to invoke a callback function once the transition was complete :
var i=10;
var callback = function(){
if(i--){
move(circle,Math.floor(Math.random()*1000),400, callback);
}
}
callback();

Yeah ofcource. Its not exactly the solution to the problem but sort of a trick.i first stored the instructions in a separate array (transitionsequence) and used a recursive callback to Callback (the callback defined in kinetic). its not very efficient method but i dont care as long as it solves the problem. :)
`function move2( i , limit) {
var obj = transitionsequence[i].object;
obj.transitionTo({
y:100,
duration: 0.3,
callback : function()
{
obj.transitionTo({
x:transitionsequence[i].x,
duration:0.3,
callback: function()
{
obj.transitionTo({
y:transitionsequence[i].y,
duration:0.3,
callback: function()
{
if(i < limit)
move2(i+1 , limit);
}
});
}
});
}
});
};`

The reason why your approach doesn't work is because the browser doesn't get an opportunity to repaint the canvas between your painting steps. Traditionally this was solved by rendering a single step (frame) and then waiting a small amount of time, but there's a new feature available in recent browsers: requestAnimationFrame, which is solving that exact problem.
Check Animating with javascript: from setInterval to requestAnimationFrame and requestAnimationFrame for Smart Animating (they also show how to create a shim for animating in browsers that don't support requestAnimationFrame).
(I don't know kinetic.js, but there might even be direct support for such a shim in it).

Related

Number-count-up animation in a non-blocking way

I am trying to count a number up to a certain target value in an animation-like style. The environment is Titanium on iOS. I do that as follows.
function countNumberUp(label) {
label.currentVal = label.currentVal ? label.currentVal : 0;
setTimeout(function() {
if (label.currentVal < label.targetVal) {
label.currentVal += 1;
label.setText(label.currentVal);
countNumberUp(label);
}
}, 5);
}
label is an instance of Ti.UI.Label.
First problem I see is that the label.setText()-method is veeery slow. Would be cool if the number counts up in a rush but it's only like 5 steps per second even if I vary the second parameter of setTimeout().
The other thing is that the animation totally blocks die Main/UI thread of iOS and the UI hardly accepts any actions until the animation has finished. Seems like setTimeout does not run in a seperate thread.
Does anyone of you know a better way to do this?
Btw. I have also tried setInterval() but it doesn't seem any better.
Try this example:
function countNumberUp(label){
var interval = setInterval(function(){
if(label.currentVal==label.targetVal){
clearInterval(interval);
}else{
label.text=label.currentVal;
label.currentVal++;
}
//remove self-calling contNumberUp(label);
}, 50);
//5ms delay its cause bad performance
}

How to stop the previous instances of the same function if it's called multiple times?

I have written a custom animation function. It usually works just fine, but when I call animate(); in rapid succession with different endCallbacks, sometimes the callbacks overlap really badly, causing the wrong action at the wrong time.
The problem is that the function instantiates multiple times and executes untill the endValue is reached. The currentValue is changed so fast that I get to see just the last value in my html page animation. This hiddes this unwanted behavior.
What I need when I call animate(); a second time is to end the first instance of animate(); and trigger a new one with new values and a new callback. Also at the same time I want to stop the setTimeout() function just to make sure no wrong callback is triggered.
window.onload = function(){
document.addEventListener('click', // some button
function (){
animate(1, 10);
}, false
);
}
function animate(startValue, endValue, callback, endCallback) {
var startValue = startValue,
currentValue = startValue,
endValue = endValue,
callback = callback,
timeout = null;
loopAnimation();
function loopAnimation(){
if (currentValue != endValue){
timeout = setTimeout(function(){
currentValue++;
// Callback executes some page manipulation code
if (typeof callback !== "undefined") callback(currentValue);
console.log(currentValue);
loopAnimation();
},500)
} else {
console.log("This callback triggers some specific changes in my page");
if (typeof endCallback !== "undefined") endCallback();
}
}
}
Instead of seeing in the console:
1,2,3, - 1,4,2,5 ... 6,9,7,10,8,9,10
I'd like to see just:
1,2,3, - 1,2 ... 7,8,9,10
However, keep in mind that because of the way I use animate() in my script I can't relly on knowing the name or scope of the input variables. This cuts me from being able to solve it myself.
While it isn't quite the implementation you're asking for, I wonder if Underscore's throttle or debounce would meet the need?
debounce will make sure your function is called no more than X times per second -- it'll still be executed once per every time called, but the subsequent calls will be delayed to meet your rate limit. So if you called animate twice in quick succession, debounce can delay the second execution until 100ms after the first or what have you.
throttle will basically ignore calls that occur during the rate limit. So if you call your animate 10 times within 100ms, you could have it throw out all but the first. (Actually, it'll do the first one, plus one at at the end of the wait period).
You don't need to use all of underscore to get these methods; I've seen people frequently copy and pasting just the debounce and/or throttle functions from underscore. If you google, you can find some standalone throttle or debounce implementations.
Throttle and debounce are commonly used in just your case, animation.
For your original spec, to actually "end the first instance of animate()" -- there's no great reliable way to do that in javascript. There's no real general purpose way to 'cancel' a function already being executed. If you can make it work with debounce or throttle, I think it will lead to less frustration.
What you need is to store the last timeout id you used. So next time you start a new animation, you clear any ongoing animation using this timeout id and clearTimeout.
I found convenient to store the interval on the function itself.
See the jsbin here :
http://jsbin.com/nadawezete/1/edit?js,console,output
window.onload = function(){
document.addEventListener('click', // some button
function (){
animate(1, 10);
}, false
);
};
function animate(startValue, endValue, callback, endCallback) {
var currentValue = startValue;
if (animate.timeout) clearTimeout(animate.timeout);
loopAnimation();
function loopAnimation(){
if (currentValue != endValue){
animate.timeout = setTimeout(function(){
console.log(currentValue);
currentValue++;
// Callback executes some page manipulation code
if (callback ) callback(currentValue);
loopAnimation();
},500);
} else {
console.log("This callback triggers some specific changes in my page");
if (endCallback) endCallback();
}
}
}

clearInterval() and setInterval() when all animations are complete

I have a function that is repeatedly being called with setInterval creating animations. If there are still animations running I need it to stop calling the function until all the animations are complete. The code I am using is as follows:
EDIT: Added coded where I am removing the animated elements from the DOM, is that the problem?
var serviceLoop = setInterval(serviceQueue, LOOP_POLL_MS); //10 ms
function serviceQueue()
{
//do some animations..
function moveMan(from, to, plane)
{
(function() {
var tmp_from = from;
var tmp_to = to;
var tmp_plane = plane;
var pos = tmp_from.offset();
var temp = tmp_from.clone(true);
temp.css({ "visibility":"visible",
"position":"absolute",
"top":pos.top + "px",
"left":pos.left + "px"});
temp.appendTo("body");
tmp_from.css("visibility", "hidden");
//if (!tmp_plane) tmp_to.css("visibility", "hidden");
temp.animate(to.offset(), moveMan.ANIMATION_TIME, function() {
tmp_to.css("visibility", "visible");
temp.remove();
});
})();
}
if ($(":animated").length > 0)
{
clearInterval(serviceLoop);
$(":animated").promise().done(function() {
serviceLoop = setInterval(serviceQueue, LOOP_POLL_MS);
});
}
}
The problem I am having is after a couple of animations the function passed to done() is never called, and the script stops.
It seems likely that you end up waiting on a promise() that is waiting on some animations, but then you remove some of those objects from the DOM and then their animation never finishes so the promise never resolves.
See this quote from the jQuery doc for .promise():
Note: The returned Promise is linked to a Deferred object stored on
the .data() for an element. Since the.remove() method removes the
element's data as well as the element itself, it will prevent any of
the element's unresolved Promises from resolving. If it is necessary
to remove an element from the DOM before its Promise is resolved, use
.detach() instead and follow with .removeData() after resolution.
One quick hack might be top call .stop(true) on any item that you are removing from the DOM.
In general, this code needs to be rewritten to be safe from that possibility and hopefully to rethink how you approach whatever it is you're trying to do every 10ms because that's generally a bad design. You should use events to trigger changes, not a 10ms polling operation. You have not explained the purpose of this code so it's not clear to me what alternative to suggest.

jQuery fadeOut callback never fires

so I'm trying to create a really simple animation using jQuery but the callback for fadeOut doesn't ever happen.
Here is the code below:
var move = function(target, speed) {
$(target).delay(200).animate({bottom: "-=20"},
speed,
function() {
$(this).fadeOut(200, function() {
$(this).css("bottom", "+=20");
});
$(this).fadeIn(200);
}
);
move(target, speed);
};
move(".fa-arrow-down", 500);
I've tried several different things like checking it out in the console, setting various breakpoints (mainly right at $(this).css("bottom", "+=20");, right above this line, and right below this line).
I'm not really sure why it's not happening. Any help is appreciated and if you could please also explain what the issue actually is so I can learn too. Here's a fiddle.
You need to move the call to move(target, speed) to inside the complete function of the fadeIn.
DEMO
var move = function (target, speed) {
$(target).delay(200).animate({
bottom: "-=20"
},
speed,
function () {
$(this).fadeOut(200, function () {
$(this).css("bottom", "+=20");
});
$(this).fadeIn(200, function () {
move(target, speed);
});
});
};
move(".fa-arrow-down", 500);
But I can't take all the credit... cookie monster was first to see the recursion error. Just like he said, this works because it adds a delay to each time you call move()
As I just said, you're invoking move() immediately instead of waiting
for the callback. This means it invokes move() at a rate of frequency
equal to the time it takes to invoke .delay().animate(), but without
waiting for them to finish. In other words, move() will be invoked
tens of thousands of times even before the first .delay() is finished -- cookie monster

Loop Through jQuery Function Inifitly

I have a jQuery Animation which I want to loop infinitely, I have the current code but it just returns nothing.
$(document).ready(function() {
var i = 0;
document.write(i);
function runTest(){
$('#page_effect').fadeIn(1500).delay(3500).fadeOut(1500);
$('#page_effect2').delay(7000).fadeIn(1500).delay(3500).fadeOut(1500);
$('#page_effect3').delay(13900).fadeIn(1500).delay(3500).fadeOut(1500);
$('#page_effect4').delay(21000).fadeIn(1500).delay(3500).fadeOut(1500);
i++;
runTest();
}
if(i === 0){
runTest();
}
});
Many Thanks! :)
You could wrap them all in a function and re-call the function after the last animation has finished:
function run(){
$('#page_effect').fadeIn(1500).delay(3500).fadeOut(1500);
$('#page_effect2').delay(7000).fadeIn(1500).delay(3500).fadeOut(1500);
$('#page_effect3').delay(13900).fadeIn(1500).delay(3500).fadeOut(1500);
$('#page_effect4').delay(21000).fadeIn(1500).delay(3500).fadeOut(1500,run);
}
run();
Live example: http://jsfiddle.net/nCs6N/
First off, you shouldn't chain animations like this, use callbacks:
$("#page_effect").fadeIn(1500, function() {
$(this).delay(3500).fadeOut(1500);
});
This will wait for 3500ms to fadeOut, but only after the 1500ms fadeIn is complete.
This way using callbacks, you can call the function again from the last callback:
function runTest(){
...
$('#page_effect4').delay(21000).fadeIn(1500, function() {
$(this).delay(3500).fadeOut(1500, function() {
runTest();
// this will be called only after the fadeout completes
});
});
}
You're queueing the animations, but never "yielding" execution back to the browser, because you just call the function again immediately.
The browser never gets to enter its event loop, and the animations won't start.
To fix this, you'll need to make the browser wait until all of the animations have completed, and then queue them again:
$(function() {
(function animate() {
$('#page_effect').fadeIn(1500).delay(3500).fadeOut(1500);
$('#page_effect2').delay(7000).fadeIn(1500).delay(3500).fadeOut(1500);
$('#page_effect3').delay(13900).fadeIn(1500).delay(3500).fadeOut(1500);
$('#page_effect4').delay(21000).fadeIn(1500).delay(3500).fadeOut(1500);
// using deferred objects, ask for a promise that will be resolved
// when all animations on the specified elements have been completed,
// and when done, call myself to start all over again
$('#page_effect,#page_effect2,#page_effect3,#page_effect4')
.promise().done(animate);
})(); // invoke immediately
});
I note that your four separate effects are probably supposed to run in series, but the .promise() based solution above will also work if they were all running in parallel.
See http://jsfiddle.net/alnitak/ZKevs/
Note that if you are intending to run those effects in series you shouldn't really just queue them up all together - there's no timing guarantee and it's possible that the next element might start animation before the previous one finished.
The traditional solution to this was to add an "animation complete" callback in the last animation, but with four separate animations as you have here that would end up being horribly nested.
jQuery deferred objects can help here, too - note how this eliminates the additional calculated .delay() calls:
$('#page_effect').fadeIn(1500).delay(3500).fadeOut(1500);
$('#page_effect').promise().done(function() {
$('#page_effect2').fadeIn(1500).delay(3500).fadeOut(1500);
});
$('#page_effect2').promise().done(function() {
$('#page_effect3').fadeIn(1500).delay(3500).fadeOut(1500);
});
$('#page_effect4').promise().done(function() {
$('#page_effect4').fadeIn(1500).delay(3500).fadeOut(1500);
});
$('#page_effect4').promise.done(animate);
At which point you can see that every animation chain is identical and can be refactored:
function cycle($els) {
var i = 0, n = $els.length;
(function next() {
var $this = $els.eq(i);
i = (i + 1) % n;
$this.fadeIn(1500).delay(3500).fadeOut(1500).promise().done(next);
})();
});
cycle($('#page_effect,#page_effect2,#page_effect3,#page_effect4'));
Don't call runTest() recursively like that, you'll exhaust the function stack.
Instead use setTimeout(runTest, 0);

Categories