我正在开发一个用于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,就应该触发第二个调用。一旦声音完成(即延迟),就会触发第三个脚本。动画完成后应触发第四个功能。第五个事件应该在事件上触发(例如点击)。
对此的技术解决方案是在前一个函数的回调中调用该函数,这有可能变得相当混乱。我喜欢一个解决方案,其中函数被称为精简版,这是一个有一点脑筋但没有编码经验的人可以锤击他们自己的脚本。你怎么解决这个问题?我对javascript很新,所以如果你能明确我会很感激。
答案 0 :(得分:2)
我认为回调是你最好的选择。它们不一定非常混乱(尽管它们当然可能使它们完全不可理解)。您可以创建每个函数来接受回调,然后使用这样的结构以可读的方式按顺序调用它们:
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();
答案 1 :(得分:2)
我会使用每个构建的解决方案。有一个适合您的需求。像jTour那样简单的东西,或者如果不能覆盖像Scriptio那样复杂的东西。 this question的部分答案也可能对您感兴趣。
修改强> 如果你不想使用预先存在的解决方案,我会做这样的事情:
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
为他们处理。与需要编写者管理回调的系统相比,这有一些很大的优势。对于脚本中的每一行,您不需要使用显式回调的唯一名称,也不需要匿名函数的无限嵌套。您不需要重新连接任何内容来更改命令的播放顺序,只需在数组中重新排列它们即可。
您的每个命令都需要在调用其回调(aka this
)之前等待其操作完成。我使用setTimeout
在小提琴中模拟这个。例如,如果您使用jQuery的.animate
作为highlight
,它会提供一个complete
处理程序,在动画完成后触发,只需粘贴this
(没有调用括号) ()
)那里。如果你使用的是jQuery UI,它有一个内置的'highlight' effect,所以你可以像这样实现它:
highlight: function (selector) {
//animation that highlights an element.
$(selector).effect('highlight', 'slow', this);
},
大多数提供动画的其他库应提供您可以使用的类似回调选项。
根据您的播放方式,控制声音的回调可能会更难。如果您使用的方法没有提供回调或轮询它以查看它是否已完成,您可能只需要向playsound
添加另一个参数,该参数将以ms为单位获取声音的长度,然后在继续之前等待很长时间:
playsound: function (soundPath, soundLength) {
//playing a sound (duh)
setTimeout(this, soundLength);
},