2013-02-26 4 views
2

Я работаю над интерактивным учебным пособием для JavaScript. Ядром инструмента является сценарий учебника. Сценарий будет вызывать различные функции, которые выполняются анимация, дикторонезависимый голос загружать новые страницы и т.д. Три вызова образца (большинство обучающих программ будет 10-100s звонков, поэтому аккуратный обзор вызовов очень желательно:Как создать сложные, последовательные события в javascript

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 в первом вызове. Третий скрипт запускается после завершения звука (т. Е. задержка). Четвертая функция должна запускаться после завершения анимации. Пятое событие должно запускаться в событии (например, щелчком).

Техническим решением для этого было бы назвать функционал n в обратном вызове предыдущей функции, это может стать довольно грязным. То, что мне нравится с решением, когда функции называются lite, это то, что кто-то с небольшим количеством мозгов, но без опыта кодирования мог бы забить собственный скрипт. Как бы вы решили это? Я довольно новичок в javascript, поэтому, если вы можете быть явным, я был бы признателен.

+1

Обратный звонок, что первое, что пришло мне в голову. Другим решением будет http://api.jquery.com/category/deferred-object/. Не уверен, что это хорошо для кого-то, у кого нет опыта кодирования. – Johan

+0

Альтернатива отложенному объекту jQuery: https://github.com/tildeio/rsvp.js –

+1

Нет, обратные вызовы - это путь. Для удобства вы можете обернуть их в [Обещания] (http://api.jquery.com/category/deferred-object/). – Bergi

ответ

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 обрабатывает это для них. Это имеет некоторые большие преимущества перед системой, которая требует, чтобы автор управлял обратными вызовами. Вам не нужно уникальное имя для каждой строки в скрипте, как при явных обратных вызовах, или бесконечное вложение анонимных функций. Вам не нужно переделывать что-либо, чтобы изменить порядок выполнения команд, вы просто физически переупорядочиваете их в массиве.

jsfiddle you can play with

Каждый из ваших команд нужно будет ждать, пока его действие должно быть сделано до того, как называет его обратного вызова (ака 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, который занимает длину звука в мс, а затем ждет этого задолго до продолжение:

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

+1 - Определенно превосходит мое решение, хорошая работа :-D – AmericanUmlaut

+0

В моем последнем редактировании я изменил способ его работы. Вместо отправки 'callback' в качестве аргумента для каждой команды он отправляется как' this', поэтому, когда каждая команда выполняется, вы должны вызывать 'this()' вместо 'callback()'. Я сделал это, чтобы сделать его менее хрупким, теперь, если человек, создающий учебник, забывает параметр, обратный вызов не будет заменен отсутствующим параметром. –

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(); 
+0

Что такое 'wrap'? – Bergi

+0

@ Bergi Heck, если я знаю, его определение подразумевается в OP, поэтому я предполагаю, что он знает, что он делает. Я предполагаю, что те же функции уже определены, что OP предполагает, что они определены, но каждый из них получил дополнительный параметр обратного вызова. – AmericanUmlaut

+0

Вы должны использовать массив, а не объект. В JS вам не гарантировано получить свойства в том порядке, в котором они были определены **, когда [перебирает объект] (https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Statements/ для ... в). –

Смежные вопросы