Do not shoot new animation until the current does not end - javascript

I have the following animation to a leaping animation
$("#bounce").click(function() {
doBounce($(this), 3, '10px', 300);
});
function doBounce(element, times, distance, speed) {
for(i = 0; i < times; i++) {
element
.animate({marginTop: '-='+distance},speed)
.animate({marginTop: '+='+distance},speed);
}
}
But, when I click several times on the element, various animation starts, thus forming a stack of events. I would like to click and while the current animation does not end, not be possible to launch other shots, even if I click on the element. How to do this?

One solution is to use the :animated selector:
function doBounce(element, times, distance, speed) {
if (element.is(":not(:animated)")) {
for(i = 0; i < times; i++) {
element
.animate({marginTop: '-='+distance},speed)
.animate({marginTop: '+='+distance},speed);
}
}
}

There a several ways, but I would suggest you use Javascript scope to set a bool that returns back to true if the animations are finished. See this Fiddle and hit test a few times when it's animating.
(function () {
var animate = true;
$("#bounce").click(function () {
// Only fire when true, which it is by default.
if (animate) {
doBounce($(this), 3, '10px', 300);
}
});
function doBounce(element, times, distance, speed) {
// Set to false to prevent new calls.
animate = false;
for (i = 0; i < times; i++) {
element.animate({
marginTop: '-=' + distance
}, speed)
.animate({
marginTop: '+=' + distance
}, speed);
}
// Set the var back to true after animations are finished.
// https://api.jquery.com/promise/
element.promise().done(function () {
animate = true;
alert(animate);
});
}
})();

One other solution is a throttle function. A throttle function wraps another (child) function and prevents calls to the child from happening more than once every n milliseconds. Here's an example using the fantastic lodash library:
var doBounce = _.throttle(function(element, times, distance, speed) {
// here, put your original doBounce code
}, lengthOfAnimation);
Now, no matter how many times you call doBounce(/* some arguments */);, the actual code won't run more than once every lengthOfAnimation milliseconds.
Of course, this will only work if the animation is the same length every time.
lodash: https://lodash.com/
throttle: https://lodash.com/docs#throttle

Related

setInterval running really slow

I made a website where I need to animate strings that are longer than the containing parent.
This is the website: Here
If you click on next, you can see multiple pages of breeders with long names, that need to animate from left to right, but this only happens after 10 or 15 seconds and it takes a long time for it to start.
Now I have checked my code and this is where I create my functions:
function newsTicker() {
console.log('newsTicker')
verifyLength();
$('.breeder').not('.short-breed-name').each(function() {
var breederNameWidth = $(this).find('.breeder_name').width();
var divBreederNameWidth = $(this).find('.breeder_name_div').width();
var diff = Math.max(parseInt(breederNameWidth - divBreederNameWidth),0);
// console.log('diff:',diff)
$(this).find('.breeder_name').animate({
marginLeft: -diff
}, 3000,
function(){
$(this).animate({
marginLeft : 0
},3000)
})
})
}
function verifyLength() {
// console.log('verifyLength')
$('.breeder.visible').each(function() {
// debugger
var breederNameWidth = $(this).find('.breeder_name').width() + 10;
var divBreederNameWidth = $(this).find('.breeder_name_div').innerWidth();
if(breederNameWidth < divBreederNameWidth) {
$(this).addClass('short-breed-name');
$(this).find('.breeder_name').css({'width':'100%','text-align':'center'})
}
})
}
And this is where I call newsTicker:
function breederAnimate(){
verifyLength();
newsTicker();
setInterval(newsTicker, 1000);
}
Why is it so slow when my times are between 1 and 3 seconds?
You should be calling setTimeout not setInterval because you only want your animation to run once. You're restarting your animations every second
Also, you should be cancelling existing setIntervals when you click next or previous

Simple loop for images

I'm trying to build a simple image slider (but using a fade effect). Every two seconds, the image should change to another image. At the end, it should call repeat_sponsor() again, to start over, so it becomes a loop.
I've written this (highly ineffective) code for 5 images. Turns out I'm going to need it for around 50 images. My editor just freezes when I add too much code.
I've tried using while-loops, but I just can't figure it out how to do this the right way.
Anyone who can help me with this?
function repeat_sponsor()
{
$("#sponsor2").hide();
$("#sponsor3").hide();
$("#sponsor4").hide();
$("#sponsor5").fadeOut("slow");
$("#sponsor1").fadeIn("slow", function() {
setTimeout(function(){$("#sponsor2").fadeIn("slow", function() {
setTimeout(function(){$("#sponsor3").fadeIn("slow", function() {
setTimeout(function(){$("#sponsor4").fadeIn("slow", function() {
setTimeout(function(){$("#sponsor5").fadeIn("slow", ...
(function (){
var cnt = 50; //set to the last one...
var max=50;
function show() {
$("#sponsor" + cnt).fadeOut("slow"); //if you want the fadeout to be done before showing next, put the following code in the complete callback
cnt++;
if(cnt>max) {
cnt=1;
}
$("#sponsor" + cnt).fadeIn("slow");
window.setTimeout(show, 2000);
}
show();
})();
But the real issue is the fact you are loading tons of images from the start. You will be better off changing it so you only have a small subset of images and change the source.
You should use some sort of for loop and a class for hiding the images. and add a max value that if checks out resets c & i
var i=0;
var c=1;
function repeat_sponsor()
{
$("#sponsor"+i).fadeOut("slow");
$(".sponsers").hide()
$("#sponsor"+c).fadeIn("slow", function() {
window.setTimeout(repeat_sponsor(), 3000);
}
i++;
c++;
}
Just run a function every two seconds with setInterval and appropriately target your different sponsor divs:
var i = 1;
var max = 50;
setInterval(function() {
// Could target all other sponsor images with a class "sponsor"
$('.sponsor').fadeOut();
// Execute code on the target
$("#sponsor" + i).fadeIn();
if (i === max) {
i = 0;
}
i++;
}, 2000);

show and hide with delays on a loop but no animation

I have a jsfiddle for this
Jsfiddle
The problem is, I am trying to create a script that ones a button is clicked flashes an image (car lights) on and off for a period of time. It works fine, but in IE8 since the lights are png the animation for it is causing a black background and border as it blinks on and off. So I trying to duplicate the same thing, but without using animation.
In my jsfiddle, the first function for the first click div represents what i am trying to do without animation, but it is not repeating. The code:
$('.oneD').click(function(){
for (var i = 0; i <= 9; i++) {
$('.oneP').show();
setTimeout(function(){
$('.oneP').hide();
}, 1000);
}
});
The 2nd function is the one I already created that does work, but it has the animation:
$('.twoD').click(function(){
for (var i = 0; i <= 9; i++) {
$(".twoP").fadeIn(1000, function () {
$(".twoP").hide();
});
}
});
Keep in mind that the jsfiddle is just a simple mock not using images. I am just looking for the functionality in which i can incorporate this. I appreciate your time in helping me with this.
instead of setTimeout() use setInterval() and clearInterval() like this:
$('.oneD').click(function(){
$('.oneP').show();
var interval = setInterval(function(){
$('.oneP').hide();
}, 1000);
//*after a number of time or loop
interval.clearInterval();
});
setInterval() "Loop" throught the function it is given every number of millisecond you pass it. and clearInterval() stop the "Loop".
I'd do it like this :
$('.oneD, .twoD').on('click', function(){
for (var i=0; i<9; i++)
$('.'+this.className.replace('D', 'P')).delay(1000).show(0)
.delay(1000).hide(0);
});
FIDDLE
This uses a selector for both elements and the same event handler, then swaps out the D for a P in the showing and hiding.
As for using delay() and making this work, hide() and show() will work just as the animated jQuery methods if a value for the duration is passed, even if that value is zero.
Fiddle here: http://jsfiddle.net/HxFpr/
var i;
$('.twoD').click(function(){
i = 0;
loopFlash();
});
function loopFlash(){
if(i < 10){ // flash 5 times (1 on 1 off = 2 cycles)
$('.twoP').toggle();
var flashing = setTimeout(loopFlash,500);
}
i++;
}
Yet another solution for you.
No Animation - with single interval
With animation - pure jQuery
http://jsfiddle.net/x6Kpv/6/
var noAnimationHandler = function() {
setInterval(function() {
var $el = $('.oneP');
$el[$el.is(":visible") ? "hide" : "show"]();
}, 800);
};
var animationHanddler = function() {
$('.twoP').fadeIn(300, function() {
$(this).delay(150).fadeOut(300, animationHanddler);
});
}
$('.oneD').click(noAnimationHandler);
$('.twoD').click(animationHanddler);
Thanks

detecting collision between two images in jquery

i have this game :http://jsfiddle.net/Qfe6L/5/
i am trying to detect when a shuriken hit an enemy so when it hit it the enemy should disappear and the score should be increased by 1 what i searched for is that i should calculate the position of the two images to check whether their is a collision but i don't seem i can do that any help from you guys ?
$(window).keypress(function (e) {
if (e.which == 32) {
CreateChuriken();
$("#Shuriken" + Shurikengid).animate({ left: '+=300px' }, 'slow');
if ($(".Shuriken").css('left') == $(".Enemy").css('left'))
{ alert("Met"); }
}
});
You need to check for collision in each animation step. Fortunately jQuery .animate() has a progress option, which you can pass a function to be called every frame.
$("#Shuriken" + Shurikengid).animate(
{ left: '+=300px' },
{ duration : 'slow',
progress: function(){
/* collision detection here */
}
}
);
Keep in mind that
if ($(".Shuriken").css('left') == $(".Enemy").css('left'))
will only compare position of first projectile and first enemy, while there are more of them on the screen. You need to iterate over every projectile and compare its powition with every enemy to find a colliding pair, like:
$('.Shuriken').each( function(){
var sOffset = $(this).offset();
$('.Enemy').each( function(){
var eOffset = $(this).offset();
if( sOffset.left == eOffset.left ){
/* boom! */
}
});
});
The above is close, but still won't work. Animation doesn't progress by 1px each frame, so you may go from Shuriken at 100px left and Enemy at 101px left at one frame to Shuriken at 102px left and Enemy at 99px left in the next one. They'll pass each other, but won't meet at the same point. So you'd need to round these values to, say, nearest 10s which will give you a bigger tolerance. You sholud also compare the vertical positions.
Updated fiddle: http://jsfiddle.net/Qfe6L/8/
(Fixed vertical posiotion of Enemies for easier testing).
Edit:
as suggested by #Kasyx it would be better to move all of this out of animation function and create a Game Loop and Scene Graph. Scene graph would keep track of elements' positions, and within Game Loop you'd check for collisions, then call a rendering function which would draw elements on screen based on the scene graph.
At the moment you’re running your hit check function once, directly after you’ve started the animation. What you need to be doing is running it every frame to see the intersect. Luckily jQuery provides a callback handler for this: $.animate’s step option. If you pass a second object into $.animate you can specify both your duration and step function like so:
$(window).keypress(function (e) {
if (e.which == 32) {
CreateChuriken();
$("#Shuriken" + Shurikengid).animate({
left: '+=300px'
}, {
duration: 'slow',
step: function(){
if ($(".Shuriken").css('left') == $(".Enemy").css('left')) {
alert("Met");
}
}
});
}
});
As you’re calling your step function once every frame you’ll want to cache your selectors inside ($('.Shuriken'), $('.Enemy')) first:
$(window).keypress(function (e) {
if (e.which == 32) {
var shuriken, enemy;
CreateChuriken();
shuriken = $('.Shuriken');
enemy = $('.Enemy');
$("#Shuriken" + Shurikengid).animate({
left: '+=300px'
}, {
duration: 'slow',
step: function(){
if (shuriken.css('left') == enemy.css('left')) {
alert("Met");
}
}
});
}
});

javascript thread

http://jsfiddle.net/andersb79/SNTb3/1/
In this example when you click run. The div is animated in after it got its 1000 hellos.
If i comment the for loop in the beginning out and comment in the other for loop the animation starts before the append is running.
But then the animation is not running smooth. Can you in some way make this happend or is it impossible in javascript.
I want it to load up the divDocument in the meantime it is animated in. I dont mean it should be live adding but I dont want it to mess upp the animation and I dont want to loose time by adding the 1000 records before the animation is started.
Hope that you understand my qustion.
If you're going to add 10000 elements in one go it will block any other activity until it has finished.
If you start the animation and then do the add, it's jerky because for a short while the system is busy adding the divs, and then suddenly it realises that it's running behind on the animation and it has to rush to catch up. Note that the animation does not get interleaved with the addition of the elements.
The best way to solve this is to add the elements in batches, using setTimeout to trigger the next batch each time.
The batching gives the background thread time to run any other events, such as the animation handlers.
See http://jsfiddle.net/alnitak/yETt3/ for a working demo using batching. The core of the sample is this:
var n = 0,
batch = 100;
function nextBatch() {
for (var i = 0; i < batch && n < 10000; ++i, ++n) {
$("#divDocument").append("<div>hello</div>");
}
if (n < 10000) {
setTimeout(nextBatch, 0);
}
}
setTimeout(nextBatch, 0);
which adds elements 100 at a time, with the setTimeout call allowing the event handling thread to jump in and process animations and other UI events.
For example you can use setTimeout
$(document).ready(function () {
$(".openWindow").click(function () {
setTimeout(function() {
var divToShow = $("#divDocument");
var windowWidth = $(window).width();
var leftOut = (windowWidth * -1);
var leftIn = windowWidth + 500;
divToShow.css({ left: leftIn });
divToShow.show();
divToShow.animate({ left: 0 }, 400, function () {
divToShow.addClass("visibleDiv");
});
}, 2000);
for (var i = 0; i < 10000; i++) {
$("#divDocument").append("<div>hello</div>");
}
});
});
If you want to run animation after that elements was add you must create function for append elements and in end of this function call animation. If you want run this as async you must use setTimeout(func, 0) for animation and for elements add.

Categories

Resources