【问题标题】:Running jQuery functions with effects in a specific order以特定顺序运行带有效果的 jQuery 函数
【发布时间】:2013-08-13 15:51:28
【问题描述】:

我在 javascript 函数中有一些 jQuery,可以更改页面上的文本并以特定时间间隔淡入和淡出。我希望函数一个接一个地运行,在每个函数完成其效果之后。

dialogueExchange1();
dialogueExchange2();
dialogueExchange3();

function dialogueExchange1() {
  $('.text-area1').text("hey");
  $('.text-area1').delay(1000).showDialogue(800, 3000).prepareDialogue(800, "hey, are you awake?");
}

function dialogueExchange2() {
  $('.text-area1').delay(900).showDialogue(800, 4000).prepareDialogue(800, "wake up").delay(900);

  $('.text-area2').text("...");
  $('.text-area2').delay(1200).showDialogue(800, 1500).fadeOut(800);
}

function dialogueExchange3() {
  $('.text-area1').delay(900).showDialogue(800, 4000).prepareDialogue(800, "let's go").delay(900);

  $('.text-area2').text("not yet");
  $('.text-area2').delay(1200).showDialogue(800, 1500).fadeOut(800);
}

showDialogueprepareDialogue 是我创建的延迟和淡入淡出文本的方法。那工作正常。基本上,我只是想让文本在特定时间后更改文本区域选择器中的文本。当前正在发生的是所有功能都在同时运行,因此同时触发了文本更改效果。我希望dialogueExchange1 做它的效果,然后当它完成时,让dialogueExchange2 做它的效果,然后当它完成时等等。

我已经尝试通过以下解决方案来处理队列、超时和回调,但我并没有完全按照我的意愿去做:

how to avoid callback chains?

How do I chain or queue custom functions using JQuery?

我过去有这个工作,只是通过将所有文本更改方法链接到一行代码中来做我想做的事情,但它看起来很糟糕。将其分解为功能并按顺序运行将使其更有条理,有助于跟踪文本更改和延迟时间。谢谢!

编辑:showDialogueprepareDialogue 按要求运行

$.fn.showDialogue = function(fadeInTime, showTextTime) {
    this.fadeIn(fadeInTime).delay(showTextTime);
    return this;
};

$.fn.prepareDialogue = function(fadeOutTime, dialogue) {
    this.fadeOut(fadeOutTime, function() {
        $(this).html(dialogue);
    });
    return this;
};

解决方案 EDIT2:感谢大家的回复,感谢whoughton 首先建议使用promise()。这是我目前的解决方案,但我确信我会在看到 Shaunak 的回答后对其进行重构并更改它。

dialogueExchange1();

function dialogueExchange1() {
    $('.text-area1').text("hey");
    $('.text-area1').delay(1000).showDialogue(800, 3000).prepareDialogue(800, "hey, are you awake?");

    $('.text-area1, .text-area2, .text-area3').promise().done(function() {
        dialogueExchange2();     
    });
}

function dialogueExchange2() {
    $('.text-area1').delay(900).showDialogue(800, 4000).prepareDialogue(800, "wake up");

    $('.text-area3').text("...");
    $('.text-area3').delay(1800).showDialogue(800, 1500).fadeOut(800);

    $('.text-area1, .text-area2, .text-area3').promise().done(function() {
        dialogueExchange3();     
    });
}

function dialogueExchange3() {
    $('.text-area1').delay(900).showDialogue(800, 4000).prepareDialogue(800, "come on let's go");

    $('.text-area2').text("hold on");
    $('.text-area2').delay(1200).showDialogue(800, 1500).fadeOut(800);
}

通过这种方式,我可以灵活地优化延迟时间以反映和模仿对话。下一个函数仅在函数中的效果完成时运行,promise() 使之成为可能。如果您想看到它的实际效果,这里是jsFiddle link

【问题讨论】:

  • 你能准备一个实际代码的小提琴吗?因为您的问题的解决方案取决于 showDialogue 和 prepareDialogue 函数中发生的情况。
  • 添加了 showDialogue 和 prepareDialogue 函数
  • 好的,我在下面添加了一个适合您的答案。我创建了一个非常简单的通用示例,所以你明白了。然后如何将其应用于您的案例。您可以使用它按您想要的顺序链接任意数量的动画函数。

标签: javascript jquery


【解决方案1】:

@whoughton 指出,这是一种使用承诺的方法:

使用 .promise() 的解决方案 (jsfiddle)

HTML:

<div class="hasEffects effect1"> Div 1</div>
<div class="hasEffects effect2"> Div 2</div>
<div class="hasEffects effect3"> Div 3</div>

<button id="animate">animate</button>

jquery:

effects1 = function(){
    $(".effect1").effect("shake", {}, 1000);
    return $(".effect1");
// or you can return $(".hasEffects"); if you are running effects on multiple elements
};

effects2 = function(){
    $(".effect2").effect("shake", {}, 1000);
    return $(".effect2");
};

effects3 = function(){
    $(".effect3").effect("shake", {}, 1000);
    return $(".effect3");
};

$("#animate").click(function(){
    runAnimations([effects1,effects2,effects3]);
});

 runAnimations = function(functionArray) {
    //extract the first function        
    var func = functionArray.splice(0, 1);

    //run it. and wait till its finished 
    func[0]().promise().done(function() {

        //then call run animations again on remaining array
        if (functionArray.length > 0) runAnimations(functionArray);
    });

}

这里是jsfiddle

这是如何工作的

在 runAnimation 函数中传递需要链接的所有函数的数组,该函数递归运行,直到所有函数完成。他们使用jquery的.promise()功能在前一个动画完成之前暂停下一个函数的执行。

每次runAnimation() 运行时,它都会提取数组中的第一个函数并运行它,在为该函数执行.proimise().done() 之后,它会再次使用剩余的数组调用runAnimation()

真正的诀窍在于了解.promise() 的工作原理。它等待在传递给它的所有选择器上运行的所有动画。因此,在单个效果功能中,您可以根据需要对任意数量的元素运行效果。只要您返回正确的选择器,您就应该很好。

例如,假设在所有这些效果中,您想在所有 3 个 div 上运行 3 种不同的效果。没关系,只需从函数中返回 $("hasEffects") 即可。 :)

你的情况

在您的特定情况下,您可以使用此示例。在所有元素上添加一个分组类,例如 class="hasEffects" 。并从您的动画函数中为它们返回一个 jquery 选择器。然后使用 runAnimation 链接它们。以下是它的外观:

function dialogueExchange1() {
   $('.text-area1').text("hey");
   $('.text-area1').delay(1000).showDialogue(800, 3000).prepareDialogue(800, "hey, are you awake?");
   return $(".hasEffects");
}

function dialogueExchange2() {
   $('.text-area1').delay(900).showDialogue(800, 4000).prepareDialogue(800, "wake up").delay(900);

   $('.text-area2').text("...");
   $('.text-area2').delay(1200).showDialogue(800, 1500).fadeOut(800);
   return $(".hasEffects");
}

function dialogueExchange3() {
   $('.text-area1').delay(900).showDialogue(800, 4000).prepareDialogue(800, "let's go").delay(900);

   $('.text-area2').text("not yet");
   $('.text-area2').delay(1200).showDialogue(800, 1500).fadeOut(800);
   return $(".hasEffects");
}

runAnimations([dialogueExchange1,dialogueExchange2,dialogueExchange3]);

runAnimations = function(functionArray){
        var func = functionArray.splice(0,1);
          func[0]().promise().done(function(){
            if(functionArray.length > 0 ) runAnimations(functionArray);
          });

    }

【讨论】:

  • 感谢 Shaunak,在看到您的解决方案之前,我一直在研究承诺,这看起来确实是可行的方法。感谢您的解决方案和深入的解释。
  • 我也很感谢您这样做,不幸的是我没有时间详细介绍,我很高兴您这样做了!干得好。
【解决方案2】:

我建议使用 promise 系统,jQuery 有自己的实现:

http://api.jquery.com/promise/

【讨论】:

  • 我一定会调查的。
【解决方案3】:

一般来说,您在 jQuery 中执行此操作的方式有两种。首先是关于 Javascript 中的异步内容。

当您调用fadeOut() 时,它就像有两个执行线程一样。其中一个线程开始对元素进行一系列更改,以使其越来越不可见。另一个线程继续执行fadeOut() 之后的命令。也许是其他屏幕元素的淡入。你需要阅读它是如何工作的,因为我使用“线程”这个词真的很松散,而且细节是不同的。但是您可以将其视为同时发生的两件事。这就是为什么您会看到所有元素同时进行动画处理。 (有时这是期望的行为。)

使动画按顺序发生的一种方法是这样。您将动画调用串在同一元素上:

$(whatever).delay(400).fadeIn(300).

因此,同一元素上的动画在开始之前等待先前排队的动画完成。

另一种方式允许多个元素依次变化:

$(whatever).delay(400).fadeOut(300, function() {
    $(whateverelse).fadeIn(400);
}

在fadeOut() 完成之前不会调用该函数。所以在另一个完成之前,fadeIn() 不会开始。

你的情况比较复杂,部分是因为这两种方法,但是可以用这两种方法来完成。您的方法可能需要接受一个附加参数,该参数是在这些动画完成时调用的函数。

注意:由于您的两种方法仅适用于“this”并返回“this”,因此它们会将动画添加到您正在处理的同一元素中。这些动画应该保持顺序。

只有当您需要一个元素中的动画来等待另一个元素的动画时,您才需要第二种形式。

注意2:我注意到这些函数会将动画挂在相同的元素上,因此您可以执行以下操作:

function dialogueExchange2() {
  $('.text-area1').delay(900).showDialogue(800, 4000).prepareDialogue(800, "wake up").delay(900,
    function() {

      $('.text-area2').text("...");
      $('.text-area2').delay(1200).showDialogue(800, 1500).fadeOut(800);
    });
}

function dialogueExchange3() {
  $('.text-area2').delay(1, function() { // this just makes #3 wait for #2 to finish
    $('.text-area1').delay(900).showDialogue(800, 4000).prepareDialogue(800, "let's go").delay(900,
      function() {

        $('.text-area2').text("not yet");
        $('.text-area2').delay(1200).showDialogue(800, 1500).fadeOut(800);
      });
  });
}

当你打电话给他们时,你会这样做:

dialogueExchange1();
dialogueExchange2();
dialogueExchange3();

【讨论】:

  • 我之前设置过这种方式,文本区域选择器通过上面的函数回调进行更改。问题是当我有多个文本区域同时更改文本时会变得混乱,并且很难跟踪所有内容和延迟时间。它也不是很动态,因为动画必须在继续之前完成,所以我不能同时更改不同的文本区域。
  • 当您说“不同的文本区域”时,您的意思是区域 1 和 2 需要能够按顺序更改,而区域 3 和 4 按自己的顺序更改?
  • 是的,我有多个文本区域,只是出现时包含文本的空 div。我希望不同区域的文本在一定时间后发生变化、出现和消失,以反映对话。这是我正在制作的游戏。
【解决方案4】:

这是一个小提琴,展示了你正在尝试做的一个非常简单的版本。我认为。 http://jsfiddle.net/wembz/

每个项目都应该在设定的延迟后运行,因为它们都是异步的。 例如,如果第一个动画的长度为 1500 毫秒,而您希望文本在该动画完成后 300 毫秒发生变化,则延迟为 1800 毫秒……希望这是有道理的。

var timeoutQueue = [],
    $ta1 = $('.text-area1'),
    $ta2 = $('.text-area2');

//Each of the DELAY_IN_MS in the functions should be the exact point in ms where the animation should occur.
timeoutQueue.push(
    setTimeout(function(){
        $ta1.text("hey");
    }, DELAY_IN_MS)
);
timeoutQueue.push(
    setTimeout(function(){
        $ta1.showDialogue(800, 3000);
    }, DELAY_IN_MS)
);
timeoutQueue.push(
    setTimeout(function(){
        $ta1.prepareDialogue(800, "hey, are you awake?");
    }, DELAY_IN_MS)
);
timeoutQueue.push(
    setTimeout(function(){
        $ta1.showDialogue(800, 4000);
    }, DELAY_IN_MS)
);
timeoutQueue.push(
    setTimeout(function(){
        $ta1.prepareDialogue(800, "wake up");
    }, DELAY_IN_MS)
);
timeoutQueue.push(
    setTimeout(function(){
        $ta2.text("...");
    }, DELAY_IN_MS)
);
timeoutQueue.push(
    setTimeout(function(){
        $ta2.showDialogue(800, 1500)
    }, DELAY_IN_MS)
);
timeoutQueue.push(
    setTimeout(function(){
        $ta2.fadeOut(800)
    }, DELAY_IN_MS)
);
timeoutQueue.push(
    setTimeout(function(){
        $ta1.showDialogue(800, 4000);
    }, DELAY_IN_MS)
);
timeoutQueue.push(
    setTimeout(function(){
        $ta1.prepareDialogue(800, "let's go");
    }, DELAY_IN_MS)
);
timeoutQueue.push(
    setTimeout(function(){
        $ta2.text("not yet");
    }, DELAY_IN_MS)
);
timeoutQueue.push(
    setTimeout(function(){
        $ta2.showDialogue(800, 1500);
    }, DELAY_IN_MS)
);
timeoutQueue.push(
    setTimeout(function(){
        $ta2.fadeOut(800);
    }, DELAY_IN_MS)
);

编辑: http://jsfiddle.net/AmqHB/

var timeoutQueue = [],
    currentDelay = 500;
    $ta1 = $('#mytext');

function newSequence(fn, expectedlength, delayafter){
    timeoutQueue.push( setTimeout(fn, currentDelay) );
    currentDelay += (expectedlength + delayafter);
}

newSequence(function(){$ta1.fadeOut(500);},500,1000);
newSequence(function(){$ta1.text("hey").fadeIn(500);},500,1000);
newSequence(function(){$ta1.fadeOut(500);},500,1000);
newSequence(function(){$ta1.text("It Can't possibly be this simple.").fadeIn(500);},500,1000);

编辑 2: http://jsfiddle.net/AmqHB/1/ 我认为没有比这更有效的方法了,但是你可以用循环的善良去。

【讨论】:

  • 我也尝试过这种方式,主要问题是我必须跟踪总超时延迟,这是一个巨大的痛苦。我宁愿不必担心。
  • 我不认为以上是理想的解决方案,因为您假设每个函数只有一个异步调用,并且在一个元素上也是如此。这两个条件都是错误的,所以这个解决方案在一般情况下不起作用。恕我直言,理想的方法是使用 $.promise 来实现,这正是他们在 jquery 中添加 promise 函数的原因!因此,重新发明轮子毫无意义。请检查我的答案。适用于单个函数中的任意数量的动画,您不需要明确地对它们进行计时。您所要做的就是传入函数数组以按照您想要的顺序进行链接。
  • Promise 是一个很好的解决方案。上述解决方案适用于任意数量的元素。 Jquery 元素不是必需的。这只是构建事件序列的一种简化方法。
【解决方案5】:

这个呢:

dialogueExchange1();


function dialogueExchange1() {
  $('.text-area1').text("hey");
  $('.text-area1').delay(1000).showDialogue(800, 3000).prepareDialogue(800, "hey, are you awake?");
dialogueExchange2();
}

function dialogueExchange2() {
  $('.text-area1').delay(900).showDialogue(800, 4000).prepareDialogue(800, "wake up").delay(900);

  $('.text-area2').text("...");
  $('.text-area2').delay(1200).showDialogue(800, 1500).fadeOut(800, function(){
  dialogueExchange3();
  });
}

function dialogueExchange3() {
  $('.text-area1').delay(900).showDialogue(800, 4000).prepareDialogue(800, "let's go").delay(900);

  $('.text-area2').text("not yet");
  $('.text-area2').delay(1200).showDialogue(800, 1500).fadeOut(800);
}

【讨论】:

  • 这只会在区域 2 开始淡入淡出后调用第三个函数。
  • 这与问题中的原始代码没有什么不同。问题是所有函数都使用异步代码。
  • 是的,在前一个函数的末尾调用下一个函数不起作用,因为异步和所有这些
  • 在回调中调用下一个函数怎么样?
  • 为什么不使用简单的 setTimeout 解决方案?
猜你喜欢
  • 1970-01-01
  • 2018-06-06
  • 1970-01-01
  • 1970-01-01
  • 2020-07-26
  • 1970-01-01
  • 2010-09-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多