Эта статья была рецензирована Томом Греко и Марком Таулером . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!
Поскольку поддержка родного элемента видео и его API довольно обширна , сейчас самое время взглянуть на способы использования видео для создания чего-то интересного и интерактивного для ваших пользователей.
В этой статье мы рассмотрим основы API, и после того, как мы получим ориентиры, мы будем работать на практическом примере. Это будет мульти-видео слайдер, в котором видео воспроизводятся одно за другим, плавно загружаются и анимируются по мере воспроизведения.
Видео API — краткое введение
По сути, когда мы говорим о Video API, мы действительно говорим о Media API — API JavaScript, который позволяет вам взаимодействовать с аудио и видео элементами на веб-странице.
Этот API реализует интерфейс под названием HTMLMediaElement, который добавляет свойства , методы и события, необходимые для поддержки основных операций, общих для аудио и видео (таких как загрузка мультимедиа, изменение позиции поиска, завершение воспроизведения и т. Д.). Он расширяется как HTMLVideoElement, так и HTMLAudioElement, которые предоставляют свои собственные специальные свойства и методы.
В основном нас интересует элемент video
поэтому мы сосредоточимся на взаимодействии с этой частью API. Чтобы ознакомиться с API, вы можете посетить страницу интерактивного примера HTML5-видео, на которой представлены наиболее часто используемые элементы.
Поддержка браузера
Хотя Chrome, Firefox, Safari, Opera и Internet Explorer поддерживают видеоэлемент, они различаются по форматам, которые они могут воспроизводить, у каждого браузера есть список поддерживаемых видеоформатов, который также может быть основан на версии самого браузера.
На момент написания статьи все современные настольные и мобильные браузеры будут воспроизводить формат mp4
. Кроме того, большинство браузеров будут иметь длительную поддержку любого (или обоих) форматов ogg
или webm
. Вот полный обзор текущего состояния поддержки.
Хотя вы можете обойтись только предоставлением версии mp4
вам, вероятно, следует включить форматы ogg
и webm
в качестве комплексного решения.
Интерактивная видео-витрина
Мы будем создавать функцию демонстрации, используя элемент video
. Наша витрина будет воспроизводить серию небольших видеоклипов подряд и запускать анимацию в определенное время. Используя такой видеоэлемент, мы исследуем некоторые его свойства, методы и события и показываем уровень контроля, которого вы можете достичь с помощью этого API.
Как всегда, вы можете найти код для этого руководства в нашем репозитории GitHub , а также демонстрацию готовой демонстрации в конце статьи.
Структурирование HTML-макета
В нашем примере с ползунком каждое видео будет воспроизводиться одно за другим, при этом во время воспроизведения будут появляться соответствующие заголовки для каждого видео. Если мы не поддерживаем воспроизведение видео / находимся на мобильном устройстве, мы вернемся к статическому изображению с заголовком текста.
Создайте контурную обертку для своего слайдера и добавьте в нее каждый раздел видео, который вы хотите определить
<!--Main video wrapper--> <div class="video-wrapper" id="video-container"> <!--first video--> <div class="video-container"></div> <!--second video--> <div class="video-container"></div> <!--Nth video--> ... </div>
Разметка для каждого раздела видео
Для каждого видео, которое мы хотим воспроизвести, нам нужно будет настроить несколько элементов, чтобы мы могли отображать видео, затемнять элементы заголовка и отслеживать общий ход видео.
<!--Progress bar--> <div class="progress-bar"> <div class="progress"> <div class="progress-inner"> <span class="progress-time"></span> <span class="progress-value"></span> </div> </div> </div> <!--Progress bar overlay--> <div class="progress-overlay"></div> <!--Video Elements--> <video preload="none"> <source src="videos/video1/video1.mp4" type="video/mp4"> <source src="videos/video1/video1.webm" type="video/webm"> <source src="videos/video1/video1.ogg" type="video/ogg"> </video> <!--Video overlay--> <div class="overlay"></div> <!--Caption Elements--> <div class="caption"> <h1 data-animation-percent="10">Amazing New Adventures</h1> <h3 data-animation-percent="20">Come visit new parts of the world</h3> <p data-animation-percent="40"> Don't wait, there is a wide world out there that you can explore! Contact us to see what we can do </p> <div class="readmore" data-animation-percent="60">Find out more</div> </div>
Элементы видео будут содержать тег video
со всеми его дочерними source
настроенными на разные типы данных, для максимальной совместимости. Узнайте больше о добавлении поддержки нескольких форматов видео .
Элементы заголовка будут содержать всю разметку, которую вы хотите добавить в процессе воспроизведения видео. Вы можете добавить что угодно, но он должен иметь атрибут data-animation-percent
со значением от 0
до 100
. Это скажет ползунку, на каком проценте завершения видео исчезать элемент.
Индикатор выполнения отображает текущий прогресс видео (как в секундах, так и в процентах). Это будет периодически обновляться. Бар будет интерактивным; Когда вы наводите курсор на него, видео приостанавливается, и вы сможете просматривать его, обновляя позицию видео.
Предоставление запасного варианта для мобильных браузеров
Чтобы помочь охватить все наши базы, мы также настроим запасной элемент, который будет использоваться в случае, если текущий браузер является мобильным устройством.
Добавьте следующую разметку непосредственно перед концом основного видеооболочки
<!--Fallback--> <div class="fallback-container"> <div class="image"></div> <div class="overlay"></div> <div class="caption"> <h1 data-animation-percent="15">This is a title</h1> <h3 data-animation-percent="25">Fallback when you dont support autoplay!</h3> <p data-animation-percent="50">Come and see a wide range of tasks and activities</p> <div class="readmore" data-animation-percent="70">Act now!</div> </div> </div>
Это будет работать аналогично нашим видео. Когда пользователь загрузит страницу, мы запустим все ее элементы заголовка, чтобы исчезнуть в зависимости от значения, установленного в атрибуте data-animation-percent
. Вместо видео мы используем фоновое изображение.
Обнаружение мобильных браузеров
Наш слайдер будет использовать autoplay
элемента video для автоматического воспроизведения первого видео. Однако большинство мобильных браузеров блокируют эту функциональность (чтобы ограничить использование данных), что означает, что наилучшей политикой является обнаружение, если мы находимся на мобильном устройстве, и отображение запасного контента соответствующим образом. Обычно мы делаем это с использованием функции обнаружения, однако обнаружение поддержки атрибута autoplay довольно сложно .
Каким бы уродливым ни был нюхающий браузер / UA, он даст нам возможность проверить текущий браузер на соответствие известному списку мобильных устройств и определить, как нам действовать дальше. Мы можем сделать это используя следующее регулярное выражение:
var mobile = navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile|IEMobile/i);
Который мы можем затем использовать так:
if(!mobile){ //main video slider functionality } else { //fallback functionality }
Текущая и следующая настройка видео
Примечание : для функциональности JavaScript я использую jQuery, поэтому возьмите себе копию и добавьте ее внизу страницы.
Когда мы довольны тем, что у нас нет мобильных устройств, мы ищем все наши элементы с помощью класса video-container
и присваиваем коллекцию нашей переменной videos
. Эти элементы будут основными обертками для каждого видео и содержания его подписи. Затем мы находим элемент video
каждого контейнера и присваиваем его локальной переменной video
.
Теперь, когда у нас есть текущее видео, нам нужно найти следующий элемент видео, чтобы мы могли запустить его, чтобы начать загрузку и воспроизвести его, когда закончится текущий.
Если у нас есть другой элемент video-container
мы назначаем его переменной nextVideo
. Если у нас нет другого элемента video-container
мы возвращаемся к первому.
var videos = $('.video-container'); videos.each(function(index){ var video = $(this).find('video'), nextVideo; if(index !== (videos.length - 1)){ nextVideo = $(this).next('.video-container').find('video'); } else { nextVideo = videos.first().find('video'); } });
Вы можете добавить столько элементов video-container
сколько захотите, и определить дополнительные видео и подписи. Слайдер найдет следующее видео и создаст цикл, который будет продолжаться бесконечно.
Начало воспроизведения видео для первого видео
Для нашего первого видео нам нужно настроить несколько вещей, чтобы мы могли все двигаться.
- Мы добавляем
active
класс в.video-container
первого видео (это.video-container
видео с относительным позиционированием, чтобы оно отображалось) - Установите для атрибута
preload
видео значениеauto
чтобы браузер начал загружать видео. -
Вызовите метод
play()
чтобы воспроизведение началось немедленно
//first video, preload and play if(index === 0){ video.parents('.video-container').addClass('active'); video[0].preload = 'auto'; video[0].play(); }
Вспомогательные переменные для видео
Для каждого видео мы должны установить несколько переменных, которые мы будем использовать позже в наших мероприятиях.
var caption = video.siblings('.caption'); var captionItems = caption.find('[data-animation-percent]'); var videoBar = video.siblings('.progress-bar'); var dragging = false; var nextLoaded = false;
-
caption
является контейнером для всех анимированных элементов заголовка -
captionItems
содержит каждый элемент, который будет анимирован во время воспроизведения -
videoBar
относится к индикатору выполнения каждого видео (используется позже во время наших событий для визуального обновления происходящего). -
dragging
будет использоваться, когда мы ищем / пропускаем видео, чтобы изменить его позицию воспроизведения -
nextLoaded
будет использоваться для того, чтобы мы знали, когда уже начали загружать следующее видео в конце текущего воспроизведения видео (не позволяет нам вызыватьload()
несколько раз)
Событие timeUpdate
Поскольку наше видео воспроизводится, оно периодически вызывает событие timeupdate . Мы будем использовать это событие для некоторой обработки:
$(video).on('timeupdate', function(){ //processing to go in here });
В обратном вызове (анонимная функция, которую мы передаем методу on
jQuery) мы хотим получить время текущего видео. Мы устанавливаем переменную videoTime
, находя currentTime видео и деля его на продолжительность, умноженную на 100. Это позволит нам узнать в процентном отношении, насколько полно видео.
var videoTime = ((this.currentTime / this.duration) * 100);
Запуск анимации для элементов заголовка
Если текущее видео имеет элементы заголовка, нам нужно проверить текущее время видео, чтобы увидеть, должны ли мы сделать элементы активными или неактивными.
Внутри нашей функции мы добавляем следующее
$(video).on('timeupdate', function(){ var videoTime = ((this.currentTime / this.duration) * 100); if(captionItems.length > 0){ captionItems.each(function(){ var item = $(this); var animTime = parseInt(item.attr('data-animation-percent')); if(videoTime >= animTime){ item.addClass('active'); } else { item.removeClass('active'); } }); if(captionItems.filter('.active').length !== 0){ caption.addClass('active'); } else { caption.removeClass('active'); } if(videoTime >= 90){ caption.removeClass('active'); captionItems.each(function(){ $(this).removeClass('active'); }); } } });
Вот разбивка того, что мы делаем
- Если длина наших
captionItems
больше0
это означает, что у нас есть элементы, которые мы должны отслеживать.- Для каждого из
captionItems
нам нужно собрать значение, хранящееся в его атрибутеdata-animation-percent
и присвоить егоanimTime
(значение должно быть от0
до100
). - Если текущий
videoTime
больше, чем наши элементыanimTime
тогда нам нужно сделать элемент активным, добавивactive
класс.
- Для каждого из
- Мы также получаем коллекцию
captionItems
и используем метод .filter jQuery, чтобы проверить, есть ли у нас какие-либоactive
элементы класса. Если мы это сделаем, мы хотим добавить активный класс в оболочку заголовка (переменнаяcaption
). - Мы также проверяем, является ли текущий
videoTime
больше90
(что означает, что мы закончили на 90%). Если мы близки к завершению, мы просматриваем все подписи и удаляем ихactive
класс, а также удаляем его изcaption
Загрузка следующего видео
В том же событии timeupdate
мы также хотим проверить продолжительность нашего текущего видео и посмотреть, следует ли нам начинать загрузку следующего видео.
if(videoTime >= 70 && nextLoaded === false){ nextVideo.preload = 'auto'; nextVideo.load(); nextLoaded = true; }
Вы можете увидеть это в действии в готовой демоверсии — следующее видео должно появиться / загрузиться на вкладке сети вашей консоли примерно на 70% завершения текущего видео, давая ему несколько секунд до того, как конечное событие вызовет вызов его для воспроизведения ,
Обновление индикатора выполнения во время воспроизведения
Так как мы хотим, чтобы индикатор выполнения часто обновлялся, мы не можем поместить его в функцию timeupdate
(поскольку браузер определяет, как вызывается offen). Вместо этого мы вызываем setInterval и присваиваем его нашей переменной videoInterval
. Каждые 100 мс мы будем вызывать функцию updateProgressAuto
и передавать текущее воспроизводимое видео.
setInterval(function(){ updateProgressAuto(video); }, 100);
Автоматическое обновление индикатора выполнения с помощью UpdateProgressAuto
Каждые 100 мсек индикатор прогресса должен будет обновляться. Мы используем эту функцию для этой цели.
function updateProgressAuto(video){ var videoBar = $(video).siblings('.progress-bar'); var videoPercent = ((video[0].currentTime / video[0].duration ) * 100); videoBar.find('.progress').css('width', videoPercent + '%'); videoBar.find('.progress-value').html(parseFloat(video[0].currentTime).toFixed(2) + ' : ' + parseFloat(video[0].duration).toFixed(2)); videoBar.find('.progress-time').html(parseInt(videoPercent) + '%'); }
Вот что происходит.
- Мы получаем элемент индикатора выполнения и
videoPercent
время видео по егоduration
и умножая на 100. - Мы обновляем ширину индикатора выполнения, чтобы отразить
currentTime
процентах - Мы обновляем текущее время / продолжительность видео, чтобы точно знать, когда оно закончится.
- Мы обновляем процент, показанный на видео панели, с текущим процентом.
Событие OnEnded
Когда текущее видео заканчивается, оно вызывает событие onEnded . Именно здесь мы переключим активный класс на контейнер nextVideo
а затем вызовем его функцию воспроизведения, чтобы начать воспроизведение.
video[0].onended = function() { nextVideo.parents('.video-container').addClass('active'); video.parents('.video-container').removeClass('active'); nextVideo[0].play(); };
Обновление воспроизведения видео вручную
В качестве дополнительной функции, слайдер также позволяет вам вручную просматривать видео, изменяя его текущее время воспроизведения при нажатии или перетаскивании (позволяя перематывать или перематывать видео).
Мы связываем несколько обработчиков событий с событиями click
, mousedown
, mouseup
и mousemove
для переменной videoBar
. Цель этого состоит в том, чтобы перевести позицию щелчка / позицию перетаскивания мыши, чтобы обновить позицию видео.
//seeking with the video bar videoBar.on('click', function(e){ updateProgressManual((e.pageX - $(this).offset().left) , video); }); //mouse moving videoBar.on('mousedown',function(e) { dragging = true; updateProgressManual(e.pageX - $(this).offset().left, video); }); //mouse up (choose time to seek to) videoBar.on('mouseup',function(e) { dragging = false; updateProgressManual(e.pageX - $(this).offset().left, video); }); //mouse dragging (actively seeking) videoBar.on('mousemove',function(e) { if(dragging === true){ updateProgressManual(e.pageX - $(this).offset().left, video); } });
Все эти события вызывают функцию updateProgressManual
, которая обновляет время воспроизведения видео. Значение, которое мы соберем, будет текущей позицией pageX
минус текущее левое offset
видеобара (что даст нам правильное положение на индикаторе выполнения)
Обновление воспроизведения вручную с помощью функции UpdateProgressManual
Эта функция вызывается, когда мы вручную ищем текущее видео. Его цель — получить позицию, на которую щелкнули, и преобразовать ее во время, к которому видео должно стремиться.
//Manually updates the video when we seek using the progress bar function updateProgressManual(progressBarPosition, video){ var videoBar = $(video).siblings('.progress-bar'); var videoPercentage = ((progressBarPosition / videoBar.outerWidth()) * 100); videoBar.find('.progress').css('width', videoPercentage + '%'); videoBar.find('.progress-value').html(parseFloat(video[0].currentTime).toFixed(2) + " : " + parseFloat(video[0].duration).toFixed(2)); videoBar.find('.progress-time').html(parseInt(videoPercentage) + '%'); video[0].currentTime = ((video[0].duration * videoPercentage) / 100); }
Вот разбивка того, что происходит
- Мы рассчитываем
videoPercentage
, получая ток, переданный в позиции, и деля его наouterWidth
времени видеобара на 100. Это скажет нам, какой процент мы хотим искать. - Мы устанавливаем ширину элемента
progress
внутриvideoBar
в процентах, к которым мы стремимся. - Мы также хотим обновить время визуального воспроизведения в баре. Это делается путем получения
currentTime
иduration
видео - Наконец, мы устанавливаем значение
currentTime
для видео, чтобы оно вызывало указанный период времени.
Дополнительная функциональность
У слайдера также есть несколько других функций, которые могут оказаться полезными.
Когда videoBar
пересекается с нашей мышью, мы расширяем индикатор выполнения, чтобы было легче нажимать / перетаскивать (чтобы искать видео). Кроме того, мы также приостанавливаем видео и активируем наше наложение. Когда мышь удаляется, все возвращается назад, начиная воспроизведение и удаляя классы.
Мы также проверяем, перемещаете ли вы мышь на область caption
и ее элементы. Если заголовок в настоящее время активен, он будет замедлять видео через свойство playbackRate
(делая воспроизведение в два раза быстрее). Когда мы убираем мышь, видео возвращается к нормальной скорости.
//When hovering over progress bar, pause video and restyle videoBar.on('mouseover', function(){ $(this).siblings('.progress-overlay').addClass('active'); $(this).addClass('expanded'); video[0].pause(); }); //When not hovering, unpause video and restyle back to normal videoBar.on('mouseout', function(){ $(this).siblings('.progress-overlay').removeClass('active'); $(this).removeClass('expanded'); video[0].play(); }); //if we have caption elements, slow playback on hover if(captionItems.length !==0){ video.parents('.video-container').on('mouseover','.caption.active', function(){ video[0].playbackRate = 0.5; }); video.parents('.video-container').on('mouseout','.caption.active', function(){ video[0].playbackRate = 1; }); }
Наш мобильный только резервный контент
Если мы на мобильном устройстве, мы не будем показывать наши видео, вместо этого мы будем показывать запасной контент. Вы можете проверить это в демонстрационной версии, установив mobile
переменную в значение true.
Большая часть функциональности похожа на то, как мы обрабатывали каждое видео. Мы ищем все его элементы и затем определяем, когда их нужно добавить.
var fallbackInterval = setInterval(function(){ currentTime = (parseInt(currentTime) + timeInterval); fallbackElements.each(function(){ var animationPercent = parseInt($(this).attr('data-animation-percent')); if((currentTime / animationDuration * 100) >= animationPercent){ $(this).addClass('active'); } else { $(this).removeClass('active'); } }); //if we have any caption elements faded in if(fallbackElements.filter('.active').length !== 0){ fallbackElements.parents('.caption').addClass('active'); } //if we have ended, finish if(currentTime >= animationDuration){ clearInterval(fallbackInterval); } }, timeInterval);
Давайте пройдемся по тому, что происходит
- Мы находим
fallback
иfallbackElements
и устанавливаемfallback
контейнер для активации. - Мы устанавливаем
currentTime
в0
что дает нам время начала. Затем мы устанавливаемanimationDuration
на5000
чтобы указать, что полная анимация займет 5 секунд. Наконец, мы устанавливаемtimeInterval
равным50
что означает, что каждые 50 мс мы будем иметь обновление. - Мы создаем наш основной цикл с помощью функции
setInterval
(присваивая его нашей переменнойfallbackInterval
). Мы устанавливаем интервал для запуска каждые 50 мс. - Внутри основного интервала мы получаем новое текущее время (добавляя
timeInterval
к переменнойcurrentTime
). - Мы проверяем каждый из элементов заголовка по отношению к проценту этого нового времени, чтобы увидеть, должны ли мы активировать этот элемент.
- Если наш
currentTime
больше нашегоanimationDuration
мыfallbackInterval
используя clearInterval, чтобы завершить все.
демонстрация
И, наконец, вот демонстрация готовой витрины. Вы можете нажать на видео, чтобы переключить воспроизведение.
Где отсюда?
Теперь, когда вы видите, как Video API можно использовать для создания интересных элементов, вы можете взять пример кода из этого и расширить его, чтобы сделать его еще более удивительным.
Есть много вещей, которые вы могли бы сделать, чтобы улучшить это, вот некоторые из них:
- Создайте более привлекательную анимацию, которая применяется к элементам заголовка по мере их появления. Вы также можете создать несколько различных анимаций на случай, если вы захотите их различить.
- Подайте совершенно другой опыт, если вы находитесь на мобильном телефоне. Может быть, у вас может быть слайдер изображений или другой интерактивный контент.
- Вы можете попытаться предоставить несколько резервных контейнеров с их собственными изображениями и контентом, чтобы имитировать то, что делает видео слайдер.
- Используйте дополнительные события, методы и свойства из API, чтобы сделать слайдер еще лучше.
Если в итоге вы создадите что-то замечательное, не стесняйтесь поделиться им с нами, я хотел бы услышать ваше мнение в комментариях ниже.