【问题标题】:Return a result from nested animation从嵌套动画返回结果
【发布时间】:2016-03-04 22:31:59
【问题描述】:

我有一个动画循环用于处理单个字母。它被包裹在一个计时器中,以创建一个延迟的偏移量。每个字母比前一个字母晚 100 毫秒。我需要弄清楚如何判断完整动画何时完成,但由于使用的嵌套类型不同,我遇到了一些麻烦。

我尝试了一些不同的方法,包括尝试从动画中返回一个值,然后是计时器,然后是 $.each 函数,但我确定这是关闭的。我还想我也许可以使用 jQuery 的 animate 函数提供的承诺,但不确定如何实现这一点。这里的任何建议将不胜感激:] 谢谢

这是我当前的代码:

var offset = 200;
//drop individual letters down out of view
    function dropLetters($letters){

        var len = $letters.length - 1;

        $letters.each(function(i){

            var $letter = $(this);

            setTimeout(function(){

                $letter.animate({ top: offset + 'px' }, 300, function(){
                    if( i >= len ){
                        return $(this).promise();
                    }
                });

            }, 100 * i );

        });

    }

编辑:对不起,我意识到我省略了偏移变量。我把它加回去了——它只是设置为 200。

另外,我意识到这个问题与另一个问题相似,但似乎也有所不同。此处提供的答案提供了其他问题中不存在的几种不同方法。

【问题讨论】:

标签: javascript jquery animation


【解决方案1】:

一种利用.promise() $.when()Function.prototype.apply()$.map().delay()的方法。注意用$.when() 替换.apply().promise() 的链接以将this 作为包含元素的jQuery 对象而不是包含.then() 处的jQuery 对象的数组返回

var offset = 100, duration = 300, delay = 100, curr = 0;

function dropLetters(elems) {
  return $.fn.promise.apply(elems, $.map(elems, function(el) {
    return $(el).delay(curr += delay).animate({top: offset + "px"}, duration)
  }))
}

dropLetters($("button")).then(function() {
  console.log("complete", this)
})
button {
  position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<button>
  0
</button>

<button>
  1
</button>

<button>
  2
</button>

或者,使用$.when().queue()

var offset = 100, duration = 300, delay = 100, curr = 0;

function dropLetters(elems) {
  return $.when(elems.queue(function (next) { 
    $(this).delay(curr += delay).animate({top:offset + "px"}, duration, next())}))
}

dropLetters($("button")).then(function() {
  console.log("complete", this)
})
button {
  position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<button>
  0
</button>

<button>
  1
</button>

<button>
  2
</button>

【讨论】:

  • 就是这样!一个更好看的 jQuery 解决方案 :) 我仍然更喜欢仅在必要时使用 jQuery ;)
  • @JuanMendes 有一个更简洁的
  • 悬念要了我的命!
  • 这不是 OP 想要的,每个字母都等待每个动画完成,然后下一个按钮下降。看我的例子
  • 是的,你之前提到过。 OP 的动画也是这样做的;或者从这个角度看来。 i + duration 将永远是 duration 比以前的迭代更多。此外,在将 .delay() 替换为 setTimeout 时,链接问题方法应返回预期结果
【解决方案2】:

您可以在每个setTimeout 调用中做出承诺,并且您不需要自己跟踪所有异步操作都已完成。

function dropLetters($letters){
  var promises = [];
  $letters.each(function(i){
    var $letter = $(this);
    promises.push(new Promise(function(resolve, reject) {
      setTimeout(function(){
        $letter.animate({ top: offset + 'px' }, 300, function(){
            resolve();
        });
      }, 100 * i );
    });               
  });
  return Promise.all(promises);
}

请注意,您可能需要Promise polyfill 请参阅http://caniuse.com/#feat=promises

与现有的检查索引相比,我的答案的一个好处是,如果您将动画更改为向后,则必须在两个地方修改代码。请参阅下面我的版本,其中字母向后(和向前)飞出。

function dropLetters($letters, backwards) {
  var promises = [];
  $letters.each(function(i) {
    var $letter = $(this);
    promises.push(new Promise(function(resolve, reject) {
      setTimeout(function() {
        $letter.animate({
          top: '-100px'
        }, 300, function() {
          resolve();
        });
      }, 100 * (backwards ? $letters.length - i : i));
    }));
  });
  return Promise.all(promises);
}

dropLetters($('p')).then(function() {
  alert('finished')
});
  
dropLetters($('span'), true).then(function() {
  alert('finished')
});
p, span {
  position: relative;
  float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>
<p>A</p>

<hr style="clear: both"/>

<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>
<span>B</span>

【讨论】:

  • 刚刚注意到,offset 在 OP 的 js 中定义在哪里?
  • 可能是全球性的?与问题无关,是吗?
  • 是的,对不起,我意识到我错过了包括偏移量。感谢关于不需要跟踪异步的提示,不知道我在做额外的工作
【解决方案3】:

你可以使用$.Deferred().resolveWith()

function dropLetters($letters) {

  var len = $letters.length - 1;

  var dfd = $.Deferred();

  $letters.each(function(i) {

    var $letter = $(this);

    setTimeout(function() {

      $letter.animate({
        top: offset + 'px'
      }, 300, function() {
        if (i >= len) {
          dfd.resolveWith($(this));
        }
      });

    }, 100 * i);

  });

  return dfd.promise()

}

【讨论】:

  • @JuanMendes 是的,有几种方法可以达到预期的效果。通常会使用.queue() 进行这样的过程。为什么每次迭代都使用Promise 构造函数? resolve() 处没有设置值? $letters 是否应该被解析为 Promise 值?虽然不确定是否应该只解析最后一个元素或收集$letters;在帖子中使用了最后一个元素
  • 一个新的承诺即​​使无序完成也仍然有效(如果 OP 更改了代码,使得第一个比最后一个花费更长的时间)。
  • @JuanMendes 上面的帖子可能可以使用 $.when().apply()$.map() 或 jQuery .promise() 重写;试图在目前可能的解决方案中尽可能少地调整原帖
  • 我的第一个答案也尽可能少地修改它。然后我意识到 OP 所做的是复制 Promise 为您所做的工作。我想远离 jQuery 特定的语法,我知道 OP 正在使用它,但是当存在标准解决方案时我讨厌使用 jQuery。不过,我希望看到一个更好看的 jQuery 解决方案
  • JuanMendes “不过,我希望看到更好看的 jQuery 解决方案” 使用单个 $.Deferred() 缺少什么?
【解决方案4】:

正如其他人所说,您可以使用 $.Deferred()` 只是为了说明我添加了一个基于给定代码的示例。 ;-)

//drop individual letters down out of view
    function dropLetters($letters){
        var deferred = jQuery.Deferred();
        
        $letters.each(function(i,elem){
            var $letter = $(elem);

            var timer = setTimeout(function(){        

                $letter.animate({ top: $letter.offset().top-100 }, 300, function(f){
                   if(i+1>=$letters.length){// last letter was animated
                       deferred.resolve();
                   }
                });
              
            }, 300*i );

        });
        return deferred;

    }
dropLetters($('p')).then(function(){alert('finished')});
p{
  position:relative;
  float:left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p><p>A</p>

【讨论】:

  • 这不是最佳解决方案,如果您使用 Promises,则不应自行跟踪异步操作何时完成
  • 您的解决方案与jsstackoverflow.com/a/35807369 有何不同?
  • @guest271314 它使用resolve 而你使用resolveWith?恕我直言,它更清楚(至少对我而言),但总的来说,几乎是一样的。如果您向后而不是向前删除字母,它也会失败。只是说......我的答案在这两种情况下都有效......
  • 好点颠倒顺序会破坏功能。所以我同意@Juans 的回答。
  • @JuanMendes 没有关注你 " 如果你向后而不是向前删除字母也会失败"
猜你喜欢
  • 2020-05-11
  • 1970-01-01
  • 1970-01-01
  • 2021-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-03
相关资源
最近更新 更多