Узнайте, как динамически внедрять JavaScript в веб-страницы

В течение многих лет я использовал браузер для своих выступлений и инсталляций, используя свой собственный простой VJ-движок. А теперь, когда вы выучите несколько простых приемов, вы тоже сможете…

Быстрое вступление

Во-первых, что такое VJ-двигатель? вы можете спросить. А может даже: что такое виджей? Википедия определяет характеристики виджея как:

Создание или манипулирование изображениями в реальном времени с помощью технологического посредничества и для аудитории в синхронизации с музыкой.

А двигатель VJ - это просто программное обеспечение, используемое для VJing.

Но зачем мне создавать свои собственные, когда существует так много двигателей VJ?

Я никогда особо не любил программы для виджея - они всегда казались раздутыми и заставляли всех выглядеть примерно одинаково. Это похоже на то, когда вы впервые взяли в руки Photoshop, и вы просто смешали кучу вещей вместе, добавили несколько фильтров, и это было круто (потому что это были 90-е). Но больше всего я хотел более тесной и лучшей интеграции между разрабатываемым контентом и входными звуковыми частотами.

В наши дни я редко являюсь виджеем, но он по-прежнему управляет большинством моих инсталляций и выступлений - везде, где мне нужно несколько визуализаций, я использую RBVJ (RB для Radarboy - это я) в качестве обертки / плеера.

RBVJ прошел через несколько итераций на протяжении многих лет, когда я перешел от Flash к Processing и, наконец, теперь к JavaScript, все с использованием одной и той же простой системы.

Я ранее использовал открытый исходный код для него и своего контента (до того, как появился Git, и я даже не знал, что существует такая вещь, как открытый исходный код). Он получил кучу наград, и я видел, как мой контент использовался во множестве мест, что было приятно. Поэтому я подумал, что пришло время снова сделать его доступным для других вместе с кучей контента, чтобы показать, как я подхожу к творческому кодированию.

Хорошо, это достаточно длинное введение. Вы говорите, покажите мне деньги!

1. Структурирование контента

По сути, движок VJ - это просто навороченный браузер и проигрыватель контента. Итак, что нам действительно нужно, так это способ извлекать и воспроизводить наш контент.

Вы можете просто выгрузить весь свой контент в папку, но эта система мне больше всего подошла, позволяя использовать простую структуру для управления с клавиатуры:

  • Сдвигайте 0–9, чтобы изменить наборы
  • Клавиши 0–9 для смены банка
  • Клавиши a-z для выбора содержания в банке.

Это дает вам 2700 файлов для работы. Если вы действительно (действительно !?) хотите большего, вы также можете удвоить это, получив доступ к еще 26 файлам на банк со сдвигом A-Z).

Как и в большинстве HTML-проектов, у меня простая структура верхнего уровня, а содержимое VJ хранится в пронумерованной структуре внутри папки art, например:

index.html
/css
/js
/art <- content goes here

Моя папка с содержимым (/ art) содержит 10 пронумерованных папок, которые я называю наборами. Внутри каждого набора есть еще 10 пронумерованных папок, представляющих банки. А внутри каждого банка находится 27 отдельных пронумерованных файлов содержимого, например:

2. Получение и воспроизведение контента

Теперь нам просто нужен способ доступа к нашим файлам, который осуществляется путем вставки содержимого в нашу индексную страницу.

А сделать это довольно просто.

Волшебство происходит в функции под названием I’ve loadJS (). Он создает тег скрипта в заголовке страницы, а затем вставляет в него некоторый JavaScript (который будет нашим контентом). Мы запускаем эту функцию с помощью нажатия клавиши (но это также может быть сигнал midi или OSC) и передаем в него имя файла содержимого, которое мы хотим. Тогда скрипт доступен на странице.

// INJECT JS ONTO PAGE
var my_script;
function loadJS(filename) {
 // delete injected JavaScript if there’s been some loaded in before
 if (my_script != undefined) 
  document.getElementsByTagName("head")[0].removeChild(my_script);
 
 // create a script element
 my_script = document.createElement(’script’);
 my_script.setAttribute("type", "text/javascript");

 // Load the file in and insert it into the page’s head tag
 my_script.setAttribute("src", filename);
 document.getElementsByTagName("head")[0].appendChild(my_script);
}

Мы отслеживаем нажатия клавиш с помощью прослушивателя событий, который вызывает функцию onKeyDown () , следующим образом:

window.addEventListener( ’keydown’, function( event ) { 
  onKeyDown( event ); 
});

Слушатель передает объект события функции, которая содержит кучу полезных вещей. Вот что нас интересует: event.keycode. Нажатие клавиши «a» дает нам код клавиши, равное 65, а нажатие клавиши «z» дает нам код клавиши, равный 90. Таким образом, мы просто вычитаем 65 из keycode , чтобы дать нам требуемый номер файла и передать это значение в функцию changeFile (), которую я покажу через немного.

Точно так же нам нужны клавиши 0–9 (коды клавиш с 48 по 57, поэтому вычтите 48) для смены банков. Мы также хотим проверить, была ли нажата клавиша Shift для загрузки наборов. Для этого у объекта события есть удобная переменная event.shiftKey, поэтому наша функция onKeyDown будет выглядеть так:

// KeyPress Stuff
function onKeyDown( event ) {
    var keyCode = event.keyCode;
   // CHANGE FILE // keys a-z
   if ( keyCode >= 65 && keyCode <= 90 ) {
      changeFile(keyCode - 65);
   // CHANGE SET AND BANK // keys 0-9
   } else if ( keyCode >= 48 && keyCode <= 57 ) {
      // Test whether the shift key is also being pressed
      if( event.shiftKey ) {
       changeBank( keyCode-48 );
      } else {
       changeSet( keyCode-48 );
      }
   }
}

Функция changeFile () просто принимает нажатие клавиши и преобразует ее в URL-адрес. Он вызывает нашу функцию loadJS (), чтобы добавить контент на страницу, и бум, мы ушли ...

Итак, наша функция changeFile () будет выглядеть так:

var current_file = 0;
var current_set = 0;
var current_bank = 0;
var art_location = "art/";
// FILE LOADER FUNCTIONS
function changeFile(file) {
  current_file = file;
  var loc = current_set + '/' + current_bank + '/' + current_file;
  var filename = contentLocation + loc + '.js';
  loadJS(filename);
  document.location.hash = loc;
 //console.log("File: " + loc);
}

У меня также есть переменная art_location на случай, если я хочу иметь разные коллекции визуальных элементов (чтобы у меня были разные папки для разных шоу и инсталляций). Я также добавляю имя файла в виде хеша (https://127.0.0.1/#set/bank/file) к URL-адресу браузера, чтобы было легче увидеть, где я нахожусь.

Наши функции changeBank () и changeSet () устанавливают current_bank и переменные current_set. Затем они просто вызывают функцию changeFile (), чтобы получить правильный файл.

Для обслуживания я также сбрасываю все счетчики - возвращая current_file обратно в 0, когда я меняю банк, и current_bank обратно в 0, когда я меняю наборы. Я знаю, что когда я меняю банк, воспроизводимый файл будет первым файлом в банке. Точно так же, когда я изменяю наборы, воспроизведение файла будет сброшено, чтобы быть первым файлом из первого банка текущего набора (current_set / 0 / 0.js).

Немного скучно, но функции на самом деле очень простые:

function changeSet(set) {
  current_set = set;
  console.log("changeSet: " + current_set);
  // reset bank number
  changeBank(0);
}

function changeBank(bank) {
current_bank = bank;
  console.log("changeBank: " + current_bank);
  // reset file number and load new file
  changeFile(0);
}

Итак, полный код вашего базового движка VJ выглядит так:

// FILE LOADER FUNCTIONS
var art_location = "/art";
var fileref;
var current_file = 0;
var current_set = 0;
var current_bank = 0;
function changeFile( file ) {
  reset()
  current_file = file;
  var loc = current_set + '/' + current_bank + '/' + current_file;
  var filename = 'art/' + loc + '.js';
  loadJS( filename );
  document.location.hash = loc;
  //console.log("File: " + loc);
}
function changeSet( set ) {
  current_set = set;
  current_bank = 0;
  console.log( "changeSet: " + current_bank );
  // reset
  changeFile( 0 );
}
function changeBank( bank ) {
  current_bank = bank;
  console.log( "changeBank: " + current_bank );
  changeFile( 0 );
}
function reset(){
  ctx.clearRect( 0, 0, w, h );
  ctx2.clearRect( 0, 0, w, h );
  ctx3.clearRect( 0, 0, w, h );
  ctx.lineCap = "butt";
}
// INJECT JS ONTO PAGE
function loadJS( filename ) {
if ( fileref != undefined ) document.getElementsByTagName( "head" )[ 0 ].removeChild( fileref );
  fileref = document.createElement( 'script' );
  fileref.setAttribute( "type", "text/javascript" );
  fileref.setAttribute( "src", filename );
  document.getElementsByTagName( "head" )[ 0 ].appendChild( fileref );
}

Все, что осталось показать вам, это то, как я структурирую фактические файлы содержимого, которые используют инкапсулированную функцию, например:

// RBVJ art
rbvj = function() {
  draw = function() {
     // do some creative coding here
  }
}();

На страницу вводится функция rbvj (). Он используется повторно, поэтому каждый раз, когда на мою страницу вставляется новый файл, память очищается от всего предыдущего содержимого.

Инкапсулируя код (см. «()» После функции), любой код внутри функции rbvj () будет запускаться автоматически, когда файл вставляется на страницу.

Вы заметите, что внутри содержимого у меня есть функция draw () (она из моего собственного служебного скрипта creative_coding.js). Это простой цикл, использующий requestAnimationFrame () JavaScript и способный изменять частоту кадров.

var frame_number = 0;
var frame_rate = 60;
var last_update = Date.now();
function loop() {
var now = Date.now();
  var elapsed_mils = now - last_update;
if ((typeof window.draw == 'function') && (elapsed_mils >= (1000 / window.frame_rate))) {
    window.draw();
    frame_number++;
    last_update = now - elapsed_mils % (1000 / window.frame_rate);
  }
  requestAnimationFrame(loop);
};

Вот и все. Теперь у вас есть работающий двигатель VJ в заправочной станции.

3. Еще кое-что, что может быть полезно

Обычно я просто подключаю звуковой вход своего компьютера прямо к входу микшера или усилителя на месте проведения мероприятия (я использую версию файла со стандартным входом для микрофона mic.js, о котором вы можете прочитать подробнее здесь) . И у меня есть настройки клавиш (в моем случае клавиши плюс и минус) для регулировки входных уровней вверх или вниз, поэтому мне не нужно постоянно получать доступ к микшеру. .

Также обратите внимание, что для ввода звука вам понадобится безопасное соединение HTTPS - или, если вы используете что-то вроде Live Server от Atom, оно уже встроено.

У меня также есть множество других ключей, настроенных для простых аудио и визуальных фильтров (см. Как сделать фильтр пикселизации здесь).

Обычно я не использую экран / интерфейс предварительного просмотра, но создать его достаточно просто. Просто создайте новую HTML-страницу и позвольте страницам общаться друг с другом через сокет.

И, наконец, последний совет: при разработке контента просто создайте функцию для чтения в текущем хеш-значении браузера и вызовите функцию loadFile () при загрузке страницы. Таким образом, когда вы работаете с файлом и перезагружаете страницу, этот файл отображается автоматически.

Вот и все. Надеюсь, это поможет вам выйти и показать больше вашего контента. Как я уже упоминал ранее, я добавил кучу материалов, с которыми вы можете поиграть и протестировать, чтобы вы могли почувствовать, как я создаю свои работы. Если вы используете или измените что-либо из этого, мне бы хотелось увидеть, как, где и что вы с ним сделали. Так что, пожалуйста, напишите мне.

Удачного кодирования. Спасибо за внимание!

Как обычно, полный код доступен на Github.

Эта статья является частью продолжающейся серии руководств по изучению творческого кодирования на чистом JavaScript. Конечно, я должен сделать это в ES6, но хотел, чтобы это было как можно проще для понимания.

Вы можете увидеть предыдущие все мои предыдущие уроки творческого кодирования здесь.

И следите за мной здесь, чтобы быть в курсе обновлений, техник и конфет: