本文是由Tom Greco和Marc Towler进行同行评审的。 感谢所有SitePoint的同行评审人员使SitePoint内容达到最佳状态!
由于对本机视频元素及其API的支持相当全面 ,因此现在是一个绝佳时机,您可以研究可以利用视频为用户创建精美且互动的方式的方法。
在整篇文章中,我们将研究API的基础知识,在掌握了相关知识之后,我们将研究一个实际示例。 这将是一个多视频滑块,其中一个接一个地播放视频,并在播放时无缝地加载内容并设置动画效果。
视频API –简介
本质上,当我们谈论视频API时,实际上是在谈论媒体API —一种JavaScript API,它使您可以与网页上的音频和视频元素进行交互。
该API实现了一个称为HTMLMediaElement的接口,该接口添加了支持音频和视频常用的基本操作(例如加载媒体,更改查找位置,完成播放等)所需的属性 , 方法和事件 。 HTMLVideoElement和HTMLAudioElement都对其进行了扩展,它们提供了自己的特殊属性和方法。
我们主要关注video元素,因此我们将专注于与API此部分的交互。 要感受一下API,您可以访问Interactive HTML5 Video Example页面,其中展示了最常用的元素。
浏览器支持
虽然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元素将包含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属性中设置的值。 我们使用的是背景图片,而不是视频。
检测移动浏览器
我们的滑块将使用video元素的autoplay属性自动播放第一个视频。 但是,大多数移动浏览器会阻止此功能(以限制数据使用),这意味着最好的策略是检测我们是否在移动设备上,并相应显示备用内容。 通常,我们将使用功能检测来执行此操作,但是, 检测对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分配给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中(此视频具有相对位置的样式,以便显示它)
- 将视频的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
});
在回调(我们将传递给jQuery的on方法的匿名函数)中,我们想要获取当前视频的时间。 我们通过找到视频的currentTime并将其除以持续时间乘以100来设置videoTime变量。这将使我们知道视频的完整程度。
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集合,并使用jQuery的.filter方法检查我们是否有任何active类项目。 如果这样做,我们想将活动类添加到字幕包装器( caption变量)。
- 我们还会检查当前的videoTime是否大于90 (这意味着我们完成了90%)。 如果我们快要结束了,我们将遍历所有标题并删除其active类,并将其从caption删除
加载下一个视频
在同一个timeupdate事件中,我们还想检查当前视频的时长,看看是否应该开始加载下一个视频。
if(videoTime >= 70 && nextLoaded === false){
nextVideo.preload = 'auto';
nextVideo.load();
nextLoaded = true;
}
您可以在完成的演示中看到它的运行情况-下一个视频应该在当前视频的大约70%完成时出现/加载到控制台的“网络”标签中,并在结束事件触发触发播放前几秒钟。
播放期间更新进度条
由于我们希望进度条经常更新,因此我们不能将其放在timeupdate函数中(因为浏览器确定了它的调用方式)。 相反,我们调用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) + '%');
}
这是怎么回事。
- 我们正在获取进度条元素,并通过将视频的currentTime乘以其duration并乘以100来找到videoPercent 。
- 我们更新进度条的宽度以反映currentTime百分比
- 我们会更新视频的当前时间/时长,以便确切知道视频何时结束。
- 我们用当前百分比更新视频栏上显示的百分比
即将结束的事件
当前视频结束时,将触发onEnded事件。 在这里,我们将活动类切换到nextVideo的容器,然后调用其play功能以开始播放
video[0].onended = function() {
nextVideo.parents('.video-container').addClass('active');
video.parents('.video-container').removeClass('active');
nextVideo[0].play();
};
手动更新视频的播放
作为一项附加功能,滑块还允许您手动浏览视频,并在单击或拖动时更改其当前播放时间(让您快退或快进视频)。
我们将多个事件处理程序绑定到videoBar变量的click , mousedown , mouseup和mousemove事件。 目的是转换鼠标的点击位置/拖动位置以更新视频的位置。
//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。这将告诉我们,我们要寻求什么样的百分比。
- 我们将videoBar内的progress元素的宽度设置为我们要寻找的百分比。
- 我们还希望更新栏中的视觉播放时间。 通过获取视频的currentTime和时duration
- 最后,我们设置视频的currentTime值以使视频在指定的时间段内搜索。
附加功能
滑块还具有一些其他有用的功能。
当videoBar与我们的鼠标相交时,我们将展开进度条,以使其更易于单击/拖动(以查找视频)。 此外,我们还暂停了视频并激活了叠加层。 当鼠标移开时,所有内容都会还原,开始播放并删除类。
我们还将检查您是否将鼠标移到caption区域及其元素上。 如果字幕当前处于活动状态,它将通过playbackRate属性减慢视频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变量)。 我们将间隔设置为每50ms运行一次。
- 在主间隔内,我们获得新的当前时间(通过将timeInterval添加到currentTime变量中)。
- 我们对照此新时间的百分比检查每个标题元素,以查看是否应激活此元素。
- 如果currentTime大于animationDuration ,则使用clearInterval清除fallbackInterval以完成所有操作。
演示版
最后,这是完成展示的演示。 您可以单击视频以切换播放。
请参阅CodePen上的SitePoint ( @SitePoint )提供的Pen Video API交互式展示 。
从这里到哪里?
现在,您已经了解了如何使用Video API创建有趣的元素,您可以从中获取示例代码并将其扩展以使其更加出色。
您可以做很多事情来改善这一点,其中一些是:
- 创建一个更精美的动画,在字幕元素淡入时将其应用到字幕元素。您还可以创建多个不同的动画,以防它们有所区别。
- 如果您使用手机,则可以提供完全不同的体验。 也许您可以具有图像滑块或其他一些交互式内容。
- 您可以尝试为多个备用容器提供自己的图像和内容,以模仿视频滑块的功能。
- 使用API中的其他事件,方法和属性可以使滑块更好。
如果您最终想与我们分享一些很棒的东西,在下面的评论中,我很乐意听取您的意见。