0

i've found a shake effect in stackoverflow question (here)
Code is like below;

jQuery.fn.shake = function(intShakes, intDistance, intDuration) {
    this.each(function() {
        $(this).css("position","relative"); 
        for (var x=1; x<=intShakes; x++) {
        $(this).animate({left:(intDistance*-1)}, (((intDuration/intShakes)/4)))
    .animate({left:intDistance}, ((intDuration/intShakes)/2))
    .animate({left:0}, (((intDuration/intShakes)/4)));
    }
  });
return this;
};

but i need a way to add a callback function (or any other simple way) for chaging border color of shaking element before the effect and toggle to orginal color after animation complate.
I tried like below but no chance (border is turning original color immediately)

jQuery.fn.shake = function(intShakes, intDistance, intDuration,callback) {
    this.each(function() {
        $(this).css("position","relative"); 
        for (var x=1; x<=intShakes; x++) {
        $(this).animate({left:(intDistance*-1)}, (((intDuration/intShakes)/4)))
    .animate({left:intDistance}, ((intDuration/intShakes)/2))
    .animate({left:0}, (((intDuration/intShakes)/4)));
    }
  });
if(callback) callback();
return this;
};

and call like this

$elem.css('borderColor','red');
$elem.shake(3,5,500,function(){
$elem.css('borderColor','#a6caf0');})

You can find a JSFiddle Example here.(If you cancel callback function in fiddle you'll see that borders become red correctly but callback fails.)
Thaks right now...

Community
  • 1
  • 1
Alper
  • 1,085
  • 3
  • 20
  • 39

2 Answers2

3

Here you go:

$.fn.shake = function ( times, distance, duration, callback ) {
    return this.css({ position: 'relative' }).each( function () {            
        for ( var i = 0, t = duration / times; i < times; i+= 1 ) {
            $( this ).
                animate({ left: -distance }, t / 3 ).
                animate({ left:  distance }, t / 3 ).
                animate({ left:         0 }, t / 4 );
        }

        $( this ).show( callback );
    });
};

And then...

$( button ).click( function () {
    $( field ).addClass( 'shaking' ).shake( 5, 10, 500, function () {
        $( this ).removeClass( 'shaking' );
    });
});

Live demo: http://jsfiddle.net/f96ff/8/

A timer is not needed. You just add the callback function to the effects queue via the (neutral) .show() method. This ensures that the callback is invoked only after all the animations (from the queue) have finished.

Additionally, I recommend a CSS class for the "shaking" state:

.shaking { border-color: red; }

Also, notice that I significantly refactored the code for $.fn.shake. I recommend you to take a few minutes of your time to analyze how I improved the code.

Šime Vidas
  • 182,163
  • 62
  • 281
  • 385
  • @alper - The reason I didn't upvote your accepted answer was because I instinctively didn't accept the `timeout` method. I hope you see this answer. – Jared Farrish Nov 12 '11 at 02:22
  • Wow it looks pretty, as i mentioned before i'am trying to learn in more depth.. Thanks for your pure (perfect) JQuery solution. – Alper Nov 12 '11 at 14:43
1

You have to set a timeout, you cant just call the callback directly because the animate function of jquery is async. So it will execute the callback directly. What can be done is set a timeout to the time of the total animation.

You also can't just use the callback of the animate function of jQuery because you are using multiple.

Solution: http://jsfiddle.net/f96ff/2/

Niels
  • 48,601
  • 4
  • 62
  • 81
  • Šime Vidas fund a better way to do it with pure JQuery, but thanks again for your rapid and working answer. – Alper Nov 12 '11 at 14:49