jqueryanimationeffectsjquery-animateeffect

How can I execute multiple, simultaneous jquery effects?


I am animating some error/validation elements on a page. I want them to bounce and be highlighted, but at the same time if possible. Here's what I'm currently doing:

var els = $(".errorMsg");
els.effect("bounce", {times: 5}, 100);
els.effect("highlight", {color: "#ffb0aa"}, 300);

This causes the elements to first bounce, and THEN be highlighted, and I'd like them to occur simultaneously. I know that with .animate() you can specify queue:false in the options, but I don't want to use animate because the pre-built effects "bounce" and "highlight" are exactly what I want.

I have tried simply chaining the calls like els.effect().effect(), and that doesn't work. I've also tried to put queue:false in the options object I pass in, and that doesn't work.


Solution

  • Ok, so this is a very custom solution that combines the bounce and highlight effects. I'd rather see some kind of jquery support for combining these more easily, specifying {queue:false}, but I don't think it's that simple.

    What I did was take the jquery.effects.bounce.js and jquery.effects.highlight.js (from jquery-ui-1.8rc3), and combine the code of the two as DaveS suggested, to create a new effect named "hibounce". In my testing, it supports all of the options of both, and they occur simultaneously. It looks great! I'm not a huge fan of solutions like this though, because of the maintenance factor. Anytime I upgrade jquery.ui, I'll have to update this file manually as well.

    Anyway, here is the combined result (jquery.effects.hibounce.js)

    (function($) {
    
    $.effects.hibounce = function(o) {
        return this.queue(function() {
            // Highlight and bounce parts, combined
            var el = $(this),
                props = ['position','top','left','backgroundImage', 'backgroundColor', 'opacity'],
                mode = $.effects.setMode(el, o.options.mode || 'show'),
                animation = {
                    backgroundColor: el.css('backgroundColor')
                };
    
            // From highlight
            if (mode == 'hide') {
                animation.opacity = 0;
            }
    
            $.effects.save(el, props);
    
            // From bounce
            // Set options
            var mode = $.effects.setMode(el, o.options.mode || 'effect'); // Set Mode
            var direction = o.options.direction || 'up'; // Default direction
            var distance = o.options.distance || 20; // Default distance
            var times = o.options.times || 5; // Default # of times
            var speed = o.duration || 250; // Default speed per bounce
            if (/show|hide/.test(mode)) props.push('opacity'); // Avoid touching opacity to prevent clearType and PNG issues in IE
    
    
            // Adjust
            $.effects.save(el, props); el.show(); // Save & Show
            $.effects.createWrapper(el); // Create Wrapper
            var ref = (direction == 'up' || direction == 'down') ? 'top' : 'left';
            var motion = (direction == 'up' || direction == 'left') ? 'pos' : 'neg';
            var distance = o.options.distance || (ref == 'top' ? el.outerHeight({margin:true}) / 3 : el.outerWidth({margin:true}) / 3);
            if (mode == 'show') el.css('opacity', 0).css(ref, motion == 'pos' ? -distance : distance); // Shift
            if (mode == 'hide') distance = distance / (times * 2);
            if (mode != 'hide') times--;
    
            // from highlight
            el
                .show()
                .css({
                    backgroundImage: 'none',
                    backgroundColor: o.options.color || '#ffff99'
                })
                .animate(animation, {
                    queue: false,
                    duration: o.duration * times * 1.3, // cause the hilight to finish just after the bounces (looks best)
                    easing: o.options.easing,
                    complete: function() {
                        (mode == 'hide' && el.hide());
                        $.effects.restore(el, props);
                        (mode == 'show' && !$.support.opacity && this.style.removeAttribute('filter'));
                        (o.callback && o.callback.apply(this, arguments));
                        el.dequeue();
                    }
                });
    
            // Animate bounces
            if (mode == 'show') { // Show Bounce
                var animation = {opacity: 1};
                animation[ref] = (motion == 'pos' ? '+=' : '-=') + distance;
                el.animate(animation, speed / 2, o.options.easing);
                distance = distance / 2;
                times--;
            };
            for (var i = 0; i < times; i++) { // Bounces
                var animation1 = {}, animation2 = {};
                animation1[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
                animation2[ref] = (motion == 'pos' ? '+=' : '-=') + distance;
                el.animate(animation1, speed / 2, o.options.easing).animate(animation2, speed / 2, o.options.easing);
                distance = (mode == 'hide') ? distance * 2 : distance / 2;
            };
            if (mode == 'hide') { // Last Bounce
                var animation = {opacity: 0};
                animation[ref] = (motion == 'pos' ? '-=' : '+=')  + distance;
                el.animate(animation, speed / 2, o.options.easing, function(){
                    el.hide(); // Hide
                    $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
                    if(o.callback) o.callback.apply(this, arguments); // Callback
                });
            } else {
                var animation1 = {}, animation2 = {};
                animation1[ref] = (motion == 'pos' ? '-=' : '+=') + distance;
                animation2[ref] = (motion == 'pos' ? '+=' : '-=') + distance;
                el.animate(animation1, speed / 2, o.options.easing).animate(animation2, speed / 2, o.options.easing, function(){
                    $.effects.restore(el, props); $.effects.removeWrapper(el); // Restore
                    if(o.callback) o.callback.apply(this, arguments); // Callback
                });
            };
            el.queue('fx', function() { el.dequeue(); });
            el.dequeue();
        });
    };
    
    })(jQuery);
    

    It can be used like any other effect now:

    var el = $("#div1");
    el.effect("hibounce", {color: "#F00", times: 5}, 100);