jQuery animation stop() triggering easeout

2019-06-07 06:17发布

This is a bit of a generic question.

If I have a jQuery animation on an element #elm, I can stop it using $('#elm').stop()

Usually the animation will have a nice easeIn, easeOut (like the default 'swing'). Calling 'stop()' stops it right away, on the spot, looking a bit clunky. Calling $('#elm').stop(true,true) makes it finish the animation right away, all the way to the end point.

Is there a generic approach to tell an element to "change the running animation so that it will stop 'soon' and 'nearby', but use the easeOut as defined first" ?

curious, *-pike

1条回答
等我变得足够好
2楼-- · 2019-06-07 06:39

Pike,

I don't know of a totally generic way to do a "gentle_stop" - one that will operate on any animation regardless of how it was intialised.

The code below, defines the function gentleStoppable() which is, in effect, a 4-parameter version of jQuery's own .animate( properties, options ) method. The extra two paramters specify the "gentle-stop" phase.

Here's the function :

function gentleStoppable($jQ, animProps, animOptions, stopProps, stopOptions) {
    var namespace = 'gentleStop';
    var dfltStopOptions = {
        duration: 'slow',//override with stopOptions.duration .
        easing: 'easeOutCubic',//override with stopOptions.easing . (See also the safety check applied after extending dfltStopOptions below).
        queue: namespace
    };
    animProps = animProps || {};
    animOptions = animOptions || {};
    stopProps = stopProps || {};
    stopOptions = stopOptions || {};
    $jQ.each(function(i, elem) {
        var $elem = $(elem);
        function killCustomEvent() { $elem.off(namespace); }
        var C = $.Callbacks('once').add(killCustomEvent);
        if(animOptions.complete) {
            C.add(animOptions.complete);
        }
        animOptions.complete = C.fire;
        stopProps = $.extend({}, animProps, stopProps);
        stopOptions = $.extend({}, animOptions, dfltStopOptions, stopOptions);
        stopOptions.easing = ($.easing && $.easing[stopOptions.easing] ) ? stopOptions.easing : 'swing';//just in case what's specifified is unavailable.
        $elem.on(namespace, function() {//custom event
            killCustomEvent();
            C.remove(killCustomEvent);//prevent killCustomEvent being called twice.
            $elem.stop(true,false).animate(stopProps, stopOptions).dequeue(namespace);
        }).animate(animProps, animOptions);
    });
}

Initiate a gentleStoppable animation as follows :

var animProps = {
    left: '300px'
};
var animOptions = {
    duration: 2000,
    complete: function() {
        console.log('complete');
    }
};
var stopProps = {
    left: '+=15px'
};
var stopOptions = {
    duration: 500,
};
gentleStoppable($e1, animProps, animOptions, stopProps, stopOptions);

Then to perform a gentle stop :

$("#elem").trigger('gentleStop');

Demo

As it stands, the function isn't perfect and I have put it through only limited testing. Here are the known issues/improvements:

  • Direction: A stopProps property of the form xxx:+=15px will unconditionally add 15px in the gentlestop phase, even if it is in the wrong direction. For example, if the corresponding animProps property was xxx:30px then +=15px would assume the target value of 30px was being approached from below. Similaraly, a value of -=15px would assume the corresponding target value was being approached from above. An improvement to gentleStoppable() would automtically adjust each stopProps property to allow its corresponding animProps property to be approached from below OR above.

  • Overshoot: A stopProps property of the form xxx:+=15px will unconditionally add 15px in the gentlestop phase, even if +15px takes the gentle animation beyond what is specified in the corresponding animProps property. This will tend to occur if the animation is stopped near its natural end. An improvement to gentleStoppable() would automtically prevent each stopProps' property from causing its corresponding animProps property being exceeded.

  • Auto-caluculation: An undoubted improvement would be for gentleStoppable() to calculate stopProps values for itself, based on animProps. This would make the function general, simpler to use and, written correctly, would effectively fix the Direction and Overshoot issues.

  • With a little more thought the code could be refactored as a jQuery plugin, which would be more convenient as it would allow method chaining and avoid the rather clumsy .trigger(...) to perform a gentle stop.

If this is of any use, then please use it.

查看更多
登录 后发表回答