我应该如何在javascript中生成复杂的、连续的事件

How should I make complex, sequential events in javascript

本文关键字:复杂 连续 事件 javascript 我应该      更新时间:2023-09-26

我正在为JavaScript开发一个交互式教程工具。该工具的核心是教程的脚本。该脚本将触发运行动画、扬声器语音加载新页面等的各种功能。三个示例调用(大多数教程将有10-100个调用,因此非常希望对调用进行简洁的概述:

wrap(); //wrap the page in an iframe
playsound('media/welcome') //playing a sound (duh)
highlight('[name=firstname]'); //animation that highlights an element.
playsound('media/welcome2');
loadpage(page2); //loading a new page

所有的调用都有一个共同点:它们都有非正常的触发器。例如,在这个简单的脚本中,一旦加载了第一个调用中的iframe,就应该触发第二个调用。第三个脚本是在声音完成后触发的。第四个功能应该在动画完成后触发。第五个事件应该在某个事件上触发(例如单击(。

对此的一个技术解决方案是在前一个函数的回调中调用该函数,这可能会变得非常混乱。我喜欢一个将函数称为lite的解决方案,这是因为一个有点头脑但没有编码经验的人可以编写自己的脚本。你将如何解决这个问题?我对javascript很陌生,所以如果你能明确一点,我会很感激。

我会使用按构建的解决方案。一定有一个能满足你的需要。像jTour这样简单的东西,或者如果不包括它的话,像Scriptio这样稍微复杂一点的东西。你可能也对这个问题的一些答案感兴趣。

编辑如果你不想使用预先存在的解决方案,我会这样做:

var runTutorial = (function () {
    // The command object holds all the different commands that can
    // be used by someone for the tutorial. Each of these commands
    // will recive a callback set as their `this`. This
    // callback should be called by your commands when they are done
    // running. The person making the tutorial won't need to know
    // about the callback, the code will handle that.
    var commands = {
        wrap: function () {
            //wrap the page in an iframe
            this();
        },
        playsound: function (soundPath, soundLength) {
            //playing a sound (duh)
            setTimeout(this, soundLength);
        },
        highlight: function (selector) {
            //animation that highlights an element.
            //I'm using jQuery UI for the animation here,
            // but most animation libraries should provide
            // a callback for when the animation is done similarly
            $(selector).effect('highlight', 'slow', this);
        },
        loadpage: function (pageUrl) {
            //loading a new page
            setTimeout(this, 500);
        },
        waitForClick: function () {
            // when we go into the click handler `this` will no
            // longer be availble to us since we will be in a
            // different context, save `this` into `that` so
            // we can call it later.
            var that = this;
            $(document).one('click', function () {
                that();
            });
        }
    },
    // This function takes an array of commands
    // and runs them in sequence. Each item in the
    // array should be an array with the command name
    // as the first item and any arguments it should be
    // called with following as the rest of the items.
    runTutorial = function (commandList) {
        var nextCommand = function () {
            if (commandList.length > 0) {
                var args = commandList.shift();
                // remove the command name
                // from the argument list
                cmd = args.shift(1);
                // call the command, setting nextCommand as `this`
                commands[cmd].apply(nextCommand, args);
            }
        }
        nextCommand();
    };
    return runTutorial;
}()); 
$('#tutorialbutton').click(function() {
    runTutorial([
        ['playsound', 'media/welcome', 1000],
        ['highlight', '[name=firstname]'],
        ['playsound', 'media/welcome2', 1500],
        ['waitForClick'],
        ['loadpage', page2],
        ['playsound', 'media/page2', 100]
    ]);
});

runTutorial函数采用一个简单的数组,其中包含按命令运行顺序排列的命令及其参数。不需要用回调来打扰编写脚本的人,runTutorial会为他们处理这些问题。与需要编写器管理回调的系统相比,这有一些很大的优势。您不需要像使用显式回调那样为脚本中的每一行指定唯一的名称,也不需要无休止地嵌套匿名函数。你不需要重新布线任何东西来改变命令的播放顺序,你只需要在数组中物理地重新排列它们。

jsfiddle你可以玩

您的每个命令都需要等待其操作完成,然后才能调用回调(也称为this(。我用setTimeout在小提琴上模拟了这个。例如,如果您将jQuery的.animate用于highlight,它提供了一个complete处理程序,该处理程序在动画完成时触发,只需将this(不带调用括号()(粘贴在那里即可。如果你使用的是jQuery UI,它有一个内置的"高亮"效果,所以你可以这样实现它:

highlight: function (selector) {
    //animation that highlights an element.
    $(selector).effect('highlight', 'slow', this);
},

大多数其他提供动画的库都应该提供类似的回调选项。

根据你播放声音的方式,控制声音的回调可能会更困难。如果你使用的方法没有提供回调或轮询它的方式来查看它是否已经完成,你可能只需要在playsound中添加另一个参数,该参数以毫秒为单位计算声音的长度,然后等待很长时间才能继续:

playsound: function (soundPath, soundLength) {
    //playing a sound (duh)
    setTimeout(this, soundLength);
},

我认为回调是最好的选择。它们不必杂乱无章(尽管这肯定会让它们完全无法理解(。您可以创建每个函数来接受回调,然后使用这样的结构以可读的方式依次调用它们:

var loadingSequence = {
    start : function() { wrap(this.playsound); },
    playsound : function() { playsound('media/welcome', this.highlight); },
    highlight : function() { highlight('[name=firstname]', this.playsound2); },
    playsound2 : function() { playsound('media/welcome2', this.loadpage); },
    loadpage : function()  { loadpage(page2); }
};
loadingSequence.start();