.animate() 的回调被调用两次 jquery

Posted

技术标签:

【中文标题】.animate() 的回调被调用两次 jquery【英文标题】:Callback of .animate() gets called twice jquery 【发布时间】:2012-02-06 03:00:43 【问题描述】:

由于我添加了一些scrollTop-animation,我的回调的某些部分被调用了两次:

$('html, body').animate(scrollTop: '0px', 300,function() 
    $('#content').load(window.location.href, postdata, function()                  
        $('#step2').addClass('stepactive').hide().fadeIn(700, function() 
            $('#content').show('slide',800);                    
        );
    );
);

它似乎只是重复了.show(),至少我不觉得load().fadeIn() 也会被第二次调用。 .show() 第一次完成后会立即重复。顺便说一下,将 scrollTop 动画速度设置为 0 并没有帮助!

我认为它与动画队列有关,但我不知道如何找到解决方法,尤其是为什么会发生这种情况。

【问题讨论】:

【参考方案1】:

要获得完成多个元素动画的单个回调,请使用延迟对象。

$(".myClass").animate(
  marginLeft: "30em"
).promise().done(function()
  alert("Done animating");
);

当您对集合调用 .animate 时,集合中的每个元素都会单独设置动画。当每一个都完成时,回调被调用。这意味着如果您为八个元素设置动画,您将获得八个回调。当这八个元素上的所有动画完成时,.promise().done(...) 解决方案会为您提供一个回调。这确实有一个副作用,如果在这八个元素中的任何一个上发生了任何 其他 动画,则回调将不会发生,直到这些动画也完成。

有关Promise 和Deferred Objects 的详细说明,请参阅jQuery API。

【讨论】:

这解决了关于我们想要在完成动画时做什么的问题,但问题是两次调用并为此获得了 +1,但我不知道动画过程是否被调用两次?! 动画过程不会被调用两次,回调会为每个选定的元素调用一次。因此,如果您选择 8 个列表项并将它们动画到左侧,则回调将执行 8 次。当所有 8 个完成时,我的解决方案会为您提供一个回调。 @KevinB,很好的解决方案,它也帮助解决了我自己的回调问题。谢谢。 我认为这是一个有趣的答案,但不幸的是它产生了意想不到的后果。承诺不只是延迟回调执行,直到 jQuery 集合的元素完成当前动画(这会很棒)。只有在集合的所有未决动画完成后才能解决。因此,如果在第一个动画仍在运行时设置了第二个动画,则用于第一个动画的回调直到第二个动画结束后才会运行。见this bin。 没错,它等待所有当前动画完成所选元素。只等待在这个链中启动的那些是不够聪明的(也不应该是)【参考方案2】:

animate 为您调用animate 的集合中的每个元素 调用一次回调:

如果提供,startstepprogresscompletedonefailalways 回调将在每个元素的基础上调用...

由于您要为两个元素(html 元素和 body 元素)设置动画,因此您会收到两个回调。 (对于任何想知道为什么 OP 为两个元素设置动画的人,这是因为动画在某些浏览器上适用于 body,而在其他浏览器上适用于 html。) p>

要在动画完成时获得 单个 回调,animate 文档指出您使用 promise 方法获取动画队列的承诺,然后使用 then 来排队回调:

$("html, body").animate(/*...*/)
    .promise().then(function() 
        // Animation complete
    );

(注意:Kevin B 在第一次提出问题时指出了这一点 in his answer。直到四年后我才发现它丢失了,添加了它,然后......然后看到了 Kevin 的回答.请给他应得的爱。我想这是公认的答案,我应该把它留在里面。)

这是一个显示单个元素回调和整体完成回调的示例:

jQuery(function($) 

  $("#one, #two").animate(
    marginLeft: "30em"
  , function() 
    // Called per element
    display("Done animating " + this.id);
  ).promise().then(function() 
    // Called when the animation in total is complete
    display("Done with animation");
  );

  function display(msg) 
    $("<p>").html(msg).appendTo(document.body);
  
);
<div id="one">I'm one</div>
<div id="two">I'm two</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

【讨论】:

是的,这就是原因,实际上我不记得为什么我曾经写过html, body。是时候重新启动我的大脑了。谢谢 可能是因为仅 html 动画在 Webkit 中不起作用,而仅 body 在 Opera 中不起作用。两者都使用将确保它始终有效,但会在 Firefox 中触发两次回调。 (我可能弄错了浏览器...) html 对于 IE 是必需的,对于大多数其他浏览器,回调将触发两次。我正在尝试解决同样的问题。 我通过创建一个标志来处理它:var ranOne = false; $('body,html').animate( scrollTop: scrollTo , scrollTime, 'swing', function () if (ranOne) ...action... ranOne = false; else ranOne = true; ); 感觉很老套,但首先必须使用“body,html”有点老套,所以。 (抱歉缺少换行符,猜测 cmets 没有显示) 难以置信!我花了几个小时试图让我的滚动动画与 $("html,body") 一起正常工作,这样它就不会触发两次,而这个答案救了我!谢谢!

以上是关于.animate() 的回调被调用两次 jquery的主要内容,如果未能解决你的问题,请参考以下文章

MonoTouch - UIView.Animate 完成回调未在按钮 touchUpInside 委托内调用

onMessage:对传入消息调用两次回调 Flutter web

论jQuery中animate方法的回调问题

函数 rowSelected 已被调用两次(选中和取消选中)

es6异步函数调用

如果选择了非零位置,则在旋转后调用 Spinner 的 onItemSelected 回调两次