jQuery动画:如何在动画执行过程中改变动画参数

8
假设您有一个长动画,您需要更改width:
var myTargetWidth = 500;
$(el).animate( { "width" : myTargetWidth }, 5000 );

动画是异步的,因此您的代码会继续运行。几秒钟后,您决定将目标 width 更改为 300。此时动画仍在运行中。
我该如何在运行中的动画中将 targetWidth 更改为不同的值?

你尝试过在动画进行中更改myTargetWidth来查看会发生什么吗?我认为它不会做任何事情,因为它已经在内部存储了,这种情况下,您将不得不查看jQuery源代码以查看如何在流程中访问该参数。 - Paul Sullivan
是的,如果答案正确,您应该接受它!您有许多未解决的问题@eeejay - superUntitled
是的,eeejay...将适用于您的答案标记为正确,这就是该网站的全部意义。此外,请查看提到步骤(而不是.stop)的答案...它几乎肯定比.stop答案更适合您的问题。 - Paul Sullivan
嘿 @eeejay,你可以接受任何一个答案!或者是因为没有一个解决了你的问题? - BlackCursor
4个回答

5

一种选择是使用jQuery animate函数中提到的step函数(API),在动画运行时检查条件。

JSFiddle示例:http://jsfiddle.net/GweLA/13/

JS

var myTargetWidth = 500;

$(document).ready(function(){
    $('.sample').animate( { "width" : myTargetWidth },{
        duration : 5000,      
        step: function(now, fx) {
            if($(this).width() > 200){
              myTargetWidth = 300;
              $(this).stop().animate({ "width" : myTargetWidth },1000);
            }
        }
    });
});

CSS

.sample{
    width:20px;
    height:100px;
    background-color:#cccccc;    
}

HTML

<div class="sample">
   width is supposed to be animated till 500 but it stops at 300
</div>

解决方案2:

经过一些研究,我发现我们可以修改传递给步进函数的 fx 参数的 startend 属性以控制动画。虽然这使得动画变得平滑了一些,但这不是一个非常整洁的方法。

示例 JSFiddle : http://jsfiddle.net/GweLA/57/

JS

var myTargetWidth = 500;
var isExecuted = false;
$(document).ready(function(){
    $('.sample').animate( { "width" : myTargetWidth },{
        duration : 5000,
        queue : false,
        step: function(now, fx) {
                 //So that fx.start and fx.end is set only once                
                if($(this).width() > 200 && $(this).width() < 203){
                    if(!isExecuted){
                        fx.start = now-65;
                        fx.end = 300;
                    }
                    isExecuted = true;
                }
              }
    });
}); 

很棒。我觉得会很难。非常好的回答,加一分赞成。 - Paul Sullivan
@Crispwind,当它达到200时,会有一个“颠簸”,因为它会停止当前动画并开始前往新值300的新动画。是否有一种方法可以修改正在运行的动画并更改其目标CSS,以便您不必停止和重新启动动画? - eeejay
@eeejay > 我添加了一种替代方案。虽然不完美! - BlackCursor
四年后,只想说第二个解决方案非常巧妙!我也没有在其他地方看到过(虽然我猜这不是一个非常普遍的问题) - rococo

2

您可以使用以下结合方式:.stop() - 停止动画。

:animated 选择器 - 检查当前元素是否正在执行动画。

试试这个:

HTML

​<div class="a">

​</div>

​<button id="check">Check Animation </button>​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

Javascript

var myTargetWidth = 300;
var $el = $('.a')
$el.animate({
    "width": myTargetWidth
}, 5000);

$('#check').on('click', function() {
    var newHeight = 300;
    if ($('.a:animated')) {
        $el.stop().animate({
            "height": newHeight
        }, 5000);
    }
});​

查看示例


0

在jquery的github上查看effects.js,你会发现在赋值后更改变量会有问题。

来源:https://github.com/jquery/jquery/blob/master/src/effects.js

注意:请注意.animate中的赋值和对象创建到Animation(工作马)。

animate: function( prop, speed, easing, callback ) {
    var empty = jQuery.isEmptyObject( prop ),
    optall = jQuery.speed( speed, easing, callback ),
    doAnimation = function() {
        // Operate on a copy of prop so per-property easing won't be lost
        var anim = Animation( this, jQuery.extend( {}, prop ), optall );

        // Empty animations resolve immediately
        if ( empty ) {
           anim.stop( true );
        }
    };

return empty || optall.queue === false ?
this.each( doAnimation ) :
this.queue( optall.queue, doAnimation );
}

//在effects.js中执行动画

function Animation( elem, properties, options ) {
    var result,
        index = 0,
        length = animationPrefilters.length,
        deferred = jQuery.Deferred().always( function() {
            // don't match elem in the :animated selector
            delete tick.elem;
        }),
        tick = function() {
            var currentTime = fxNow || createFxNow(),
                remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
                // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
                temp = remaining / animation.duration || 0,
                percent = 1 - temp,
                index = 0,
                length = animation.tweens.length;

            for ( ; index < length ; index++ ) {
                animation.tweens[ index ].run( percent );
            }

            deferred.notifyWith( elem, [ animation, percent, remaining ]);

            if ( percent < 1 && length ) {
                return remaining;
            } else {
                deferred.resolveWith( elem, [ animation ] );
                return false;
            }
        },
        animation = deferred.promise({
            elem: elem,
            props: jQuery.extend( {}, properties ),
            opts: jQuery.extend( true, { specialEasing: {} }, options ),
            originalProperties: properties,
            originalOptions: options,
            startTime: fxNow || createFxNow(),
            duration: options.duration,
            tweens: [],
            createTween: function( prop, end ) {
                var tween = jQuery.Tween( elem, animation.opts, prop, end,
                        animation.opts.specialEasing[ prop ] || animation.opts.easing );
                animation.tweens.push( tween );
                return tween;
            },
            stop: function( gotoEnd ) {
                var index = 0,
                    // if we are going to the end, we want to run all the tweens
                    // otherwise we skip this part
                    length = gotoEnd ? animation.tweens.length : 0;

                for ( ; index < length ; index++ ) {
                    animation.tweens[ index ].run( 1 );
                }

                // resolve when we played the last frame
                // otherwise, reject
                if ( gotoEnd ) {
                    deferred.resolveWith( elem, [ animation, gotoEnd ] );
                } else {
                    deferred.rejectWith( elem, [ animation, gotoEnd ] );
                }
                return this;
            }
        }),
        props = animation.props;

    propFilter( props, animation.opts.specialEasing );

    for ( ; index < length ; index++ ) {
        result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
        if ( result ) {
            return result;
        }
    }

    createTweens( animation, props );

    if ( jQuery.isFunction( animation.opts.start ) ) {
        animation.opts.start.call( elem, animation );
    }

    jQuery.fx.timer(
        jQuery.extend( tick, {
            anim: animation,
            queue: animation.opts.queue,
            elem: elem
        })
    );

    // attach callbacks from options
    return animation.progress( animation.opts.progress )
        .done( animation.opts.done, animation.opts.complete )
        .fail( animation.opts.fail )
        .always( animation.opts.always );
}

因此,我认为你将不得不使用.stop并重新排队动画,以改变变量,因为你几乎没有希望访问闭包中的任何变量(请有经验的人纠正我)


0

我在这里添加了一个例子:

http://jsfiddle.net/Q3ZcQ/

尝试在动画中间点击“点击此处”元素...

我使用clearQueue()stop()函数。

CSS #block { width:100px; height: 100px; background: red; }​

HTML

<p>
  <a id="clickme">Click here</a>
</p>

jQUERY

$('#clickme').not('.again').on('mouseup',function() {
  $(this).addClass('again');
  $('#block').animate({
    width: '400'
  }, 5000, function() {
    // Animation complete.
  });
});

$('.again').live('mousedown', function() {
    $(this).removeClass('again');
 $('#block').clearQueue().animate({
    width: '200',
  }, 500, function() {
     $('#block').stop(true, false);
  });
});​
<div id="block" />​

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接