Partilhar via


Создание интерактивного интерфейса Vyclone с использованием HTML5

Создание интерактивного интерфейса Vyclone с использованием HTML5

Интернет становится более вовлекающим и полезным для потребителей, когда разработчики используют весь потенциал HTML5. В этой "приглашенной" записи блога Антон Молледа (Anton Molleda) из Plain Concepts рассказывает о своем опыте разработки Vyclone — социального сайта для видеомонтажа, созданного на основе HTML5 и множества новых компонентов браузеров следующего поколения, таких как Internet Explorer 10. При создании Vyclone использованы такие возможности, как события указателя, мультисенсорные жесты, canvas и CSS3 с аппаратным ускорением. Благодаря этому данный веб-сайт во многом похож на приложение.

— Роб Мосери (Rob Mauceri), руководитель группы программ, Internet Explorer

Привет!

Меня зовут Антон Молледа. Я работаю в компании Plain Concepts. В течение нескольких последних месяцев группа разработчиков Internet Explorer работала вместе с потрясающей командой, создающей популярный и перспективный социальный стартап, связанный с обработкой видео, — Vyclone. Как веб-разработчик я трепетно отношусь к расширению границ возможного в Интернете. К счастью, мне повезло принять участие в работе над этим проектом. И сегодня я хочу поделиться с вами некоторыми основными выводами, к которым мы пришли, работая над созданием видеоредактора в Интернете для Vyclone с использованием только HTML5 и JavaScript!

Vyclone — это социальная видеоплатформа, позволяющая совместно создавать, синхронизировать и редактировать множество видеозаписей одного и того же момента легко и непринужденно.

Когда сайт Vyclone был впервые запущен, он предназначался только для мобильных устройств. Но вскоре всем стало ясно, что несмотря на преимущества видеосъемки с помощью телефона, возможности обработки и монтажа этого видео ограничены вследствие малого размера экрана и ограниченной вычислительной мощности устройства. Благодаря прогрессу, достигнутому за последние несколько лет в современных браузерах, HTML5 дал реальную возможность создать этот новый инструмент.

Ядро веб-редактора Vyclone состоит из трех частей:

Окно предварительного просмотра видео: здесь можно просматривать версию видеоролика, создаваемого пользователем, в низком качестве (слева).

Видеосетка: здесь представлены все доступные источники с отображением заданной точки и времени (справа).

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

Веб-редактор Vyclone

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

При создании этих компонентов мы столкнулись с очень интересной проблемой, связанной с большим количеством операций с видео, производительностью и опытом пользователя. Давайте подробнее рассмотрим, что мы сделали для создания такого веб-интерфейса. Итак, мы используем video, canvas и requestAnimationFrame (RAF). У нас есть видео, которое воспроизводится в фоновом режиме, и при каждом вызове RAF мы отрисовываем активный источник на элементе canvas (в окне предварительного просмотра видео) или вычисляем его новый размер и положение на видеосетке.

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

А что, если такие события могут возникать десятки или даже сотни раз в секунду? И если обработчикам таких событий необходимо обновлять пользовательский интерфейс? Неужели нам необходимо обновлять разметку 130 раз в секунду при том, что изменения иногда составляют меньше пикселя? В таких случаях говорят, что "все тянется мучительно долго"!

Если ваш компьютер снабжен процессором i7 с 8 ГБ ОЗУ, то вам, вероятно, хватит вычислительной мощности для такой обработки событий. Но как быть владельцам менее мощных компьютеров? Или владельцам ARM-устройств? Таким пользователям не будет обеспечен такой же опыт работы, для них веб-сайт будет работать медленно.

Нашим первым решением была постановка действия в очередь в RAF, но этот подход был связан с несколькими проблемами. Например, вы можете вызывать RAF для одной и той же функции для одного и того же "момента", что ухудшит и без того сложную ситуацию. Для решения этой проблемы мы в первую очередь решили использовать переменную, которая будет "сообщать" нам, было ли действие уже поставлено в очередь. Код аналогичен следующему:

 var queued = false; function myAction(){ //your awesome code here queued = false; } function onEvent(evt){ if(!queued){ queued = true; requestAnimationFrame(myAction); } }

Этот код неплох, но и с ним есть несколько проблем. Если вы делаете что-то, связанное с положением события (мыши или указателя) и приращением, этот подход может вам мешать. Решение, использованное нами при создании временной шкалы, заключается в накоплении значения события и его обработке в myAction:

 var deltaX = 0, queued = false; function myAction(){ //your awesome code here uses deltaX deltaX = 0; // we reset the deltaX so it can be incremented  // next time onEvent is executed queued = false; } function onEvent(evt){ if(!queued){ queued = true; deltaX = evt.translationX; // in the case of a pointer, if you are  // using a mouse you will have to do some  // magic with pageX or similar :) requestAnimationFrame(myAction); }else{ deltaX += evt.translationX; } }

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

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

Мы видели, что код неудобен и мы теряем некоторые циклы в ожидании выполнения других действий. Поэтому мы решили снова изменить способ обработки ввода. Именно в этот момент у нас родилась идея использовать игровой цикл. Если вы не знакомы с архитектурой игр (простых), запомните, что игровой цикл — это по сути непрерывный цикл, который выполняется независимо от действий пользователя и разбивается на части при возникновении различных событий и выполнении различных действий. Согласно посвященной программированию игр статье Википедии, упрощенный игровой цикл в псевдокоде мог бы выглядеть следующим образом:

 while( user doesn't exit ) check for user input run AI move enemies resolve collisions draw graphics play sounds end while

Именно это нам и требовалось. Используя преимущества RAF, мы создали функцию tick, которая выполняется непрерывно, и внутри этой функции tick мы решаем, что нужно делать в зависимости от предшествующего пользовательского ввода или других факторов.

Упрощенная функция tick для видеосетки выглядит приблизительно следующим образом:

 function tick(){ //we clean if we've changed the size of the quadrant if(needsClean){ cleanCanvas(); } // if we have to change the quadrant's frame because we are  // the active one (or the opposite) if(newFrame){ drawFrame(); // we draw just the frame in a separate canvas so it // doesn't need to be calculated all the time, and it  // is still faster than copying from an image } //we draw the new frame if we are playing or seeking if(dirty){ draw(); drawFrameInQuadrant(); } requestAnimationFrame(tick); }

Значения needsClean, newFrame и dirty обновляются в обработчиках событий (когда пользователь осуществляет поиск, воспроизводит видео и т. д.).

Этот скачок в нашем понимании обработки действий пользователя (переход к схеме игрового цикла) позволил нам улучшить производительность и упростить наш код в редакторе.

Основной вывод: если вы разрабатываете нечто, что требует высокой интерактивности и получает много данных через пользовательский ввод, подумайте о том, как использование игрового цикла может упростить вам жизнь! Мы испытали это на себе. И если вам еще не представилась возможность поработать с отличным новым веб-редактором Vyclone (поверьте моему слову), спешите это исправить! Щелкните "Remix" для любого видео на сайте Vyclone.com, и вы увидите наш веб-редактор. Он одинаково хорошо работает при использовании как мыши, так и касаний. Я очень рекомендую поработать с ним на Surface Pro!

Наслаждайтесь! Если у вас есть вопросы, задайте их в комментариях к этой записи.

— Антон Молледа (Anton Molleda), Plain Concepts