以特定顺序运行具有效果的jQuery函数

时间:2013-08-13 15:51:28

标签: javascript jquery

我在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:感谢大家的回应,并首先建议使用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

5 个答案:

答案 0 :(得分:6)

这是一种用承诺做到这一点的方法,如@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);
          });

    }

答案 1 :(得分:2)

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

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

答案 2 :(得分:1)

一般来说,在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();

答案 3 :(得分:0)

这是一个小提琴,展示了你想要做的非常简单的版本......我想。 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/ 我认为没有更高效的东西,但在这里你可以选择优雅的环境。

答案 4 :(得分:-1)

怎么样:

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);
}