--- title: 视频和音频API slug: Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs translation_of: Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs ---
HTML5提供了用于在文档中嵌入富媒体的元素 — {{htmlelement("video")}}和{{htmlelement("audio")}} — 这些元素通过自带的API来控制视频或音频的播放,定位进度等。本文将向你展示如何执行一些常见的任务,如创建自定义播放控件。
前提: | JavaScript基础(见JavaScript第一步,创建JavaScript代码块,JavaScript对象入门),Web API简介 |
---|---|
目标: | 学习如何通过浏览器API来控制视频和音频的播放。 |
{{htmlelement("video")}}和{{htmlelement("audio")}}元素允许我们把视频和音频嵌入到网页当中。就像我们在音频和视频内容文中展示的一样,一个典型的实现如下所示:
<video controls> <source src="rabbit320.mp4" type="video/mp4"> <source src="rabbit320.webm" type="video/webm"> <p>Your browser doesn't support HTML5 video. Here is a <a href="rabbit320.mp4">link to the video</a> instead.</p> </video>
上述代码将会在浏览器内部创建一个如下图所示的视频播放器:
{{EmbedGHLiveSample("learning-area/html/multimedia-and-embedding/video-and-audio-content/multiple-video-formats.html", '100%', 380)}}
你可以点击上面的文章链接来查看相关HTML元素的所有特性;但在这篇文章中,主要目的是学习我们最感兴趣的{{htmlattrxref("controls", "video")}}属性,它会启用默认的播放设置。如果没有指定该属性,则播放器中不会显示相关控件:
{{EmbedGHLiveSample("learning-area/html/multimedia-and-embedding/video-and-audio-content/multiple-video-formats-no-controls.html", '100%', 380)}}
至此,你可能会觉得这个属性作用不大,但是它确实很有优势。使用原生浏览器控件的一个很大的问题在于,它们在各个浏览器中都不相同 — 对于跨浏览器的支持并不是很好!另一个问题是,在大多数浏览器中原生控件难以通过键盘来操作。
你可以通过隐藏本地控件(通过删除controls属性),然后使用HTML,CSS和JavaScript编写自己的代码来解决这两个问题。 在下一节中,我们将看到如何通过一些可用的工具来实现。
作为HTML5规范的一部分,{{domxref("HTMLMediaElement")}} API提供允许你以编程方式来控制视频和音频播放的功能—例如 {{domxref("HTMLMediaElement.play()")}}, {{domxref("HTMLMediaElement.pause()")}},等。该接口对{{htmlelement("audio")}}和{{htmlelement("video")}}两个元素都是可用的,因为在这两个元素中要实现的功能几乎是相同的。让我们通过一个例子来一步步演示一些功能。
我们最终的示例(和功能)将会如下所示:
{{EmbedGHLiveSample("learning-area/javascript/apis/video-audio/finished/", '100%', 360)}}
想要使用这个示例的代码来入门,请下载media-player-start.zip 并解压到您的硬盘上的一个新建目录里。如果想要下载examples repo,它位于javascript/apis/video-audio/start/
路径下。
下载并解压之后,如果您加载这个HTML,你将会看到一个通过浏览器原生播放控件渲染的非常一般的HTML5视频播放器。
打开HTML index文件。你将看到一些功能;HTML由视频播放器和它的控件所控制:
<div class="player"> <video controls> <source src="video/sintel-short.mp4" type="video/mp4"> <source src="video/sintel-short.mp4" type="video/webm"> <!-- fallback content here --> </video> <div class="controls"> <button class="play" data-icon="P" aria-label="play pause toggle"></button> <button class="stop" data-icon="S" aria-label="stop"></button> <div class="timer"> <div></div> <span aria-label="timer">00:00</span> </div> <button class="rwd" data-icon="B" aria-label="rewind"></button> <button class="fwd" data-icon="F" aria-label="fast forward"></button> </div> </div>
<button>
都有一个class名
, 一个data-icon
属性来决定在每个按钮上显示什么图标 (在下一节讲述它是如何工作的), 和一个aria-label
属性为每一个按钮提供容易理解的描述, 即使我们没有在tags内提供可读的标签。当用户关注这些元素时含有aria-label
属性的内容也会被讲述人读出来。<div>
用来创建一个水平的随着时间增加而增长的进度条。要想了解完成版本看上去是咋样的, 点击查看完成版本.现在打开CSS文件来查看里面的内容。例子中的CSS样式并不是很复杂, 我们突出了最主要的一部分。首先注意.controls
的样式:
.controls { visibility: hidden; opacity: 0.5; width: 400px; border-radius: 10px; position: absolute; bottom: 20px; left: 50%; margin-left: -200px; background-color: black; box-shadow: 3px 3px 5px black; transition: 1s all; display: flex; } .player:hover .controls, player:focus .controls { opacity: 1; }
hidden
的自定义控件{{cssxref("visibility")}}开始。稍后在我们的JavaScript中, 我们将控件设置为 visible
, 并且从<video>
元素中移除controls
属性。这是因为, 如果JavaScript由于某种原因没有加载, 用户依然可以使用原生的控件播放视频。opacity
设置为0.5 {{cssxref("opacity")}},这样当您尝试观看视频时,它们就不会分散注意力。 只有当您将鼠标悬停/聚焦在播放器上时,控件才会完全不透明。接下来,让我们看看我们的按钮图标:
@font-face { font-family: 'HeydingsControlsRegular'; src: url('fonts/heydings_controls-webfont.eot'); src: url('fonts/heydings_controls-webfont.eot?#iefix') format('embedded-opentype'), url('fonts/heydings_controls-webfont.woff') format('woff'), url('fonts/heydings_controls-webfont.ttf') format('truetype'); font-weight: normal; font-style: normal; } button:before { font-family: HeydingsControlsRegular; font-size: 20px; position: relative; content: attr(data-icon); color: #aaa; text-shadow: 1px 1px 0px black; }
首先在CSS的最上方我们使用 {{cssxref("@font-face")}} 块来导入自定义Web字体。这是一种图标字体 —— 字母表中的所有字符都是各种常用图标,你可以尝试在程序中调用。
接下来,我们使用这些内容来显示每个按钮上的图标:
data-icon
属性的内容。例如在播放按钮的情况下,data-icon
包含大写的“P”。图标字体非常酷有很多原因 —— 减少HTTP请求,因为您不需要将这些图标作为图像文件下载。同时具有出色的可扩展性,以及您可以使用文本属性来设置它们的样式 —— 例如 {{cssxref("color")}} 和 {{cssxref("text-shadow")}}。
最后让我们来看看进度条的 CSS:
.timer { line-height: 38px; font-size: 10px; font-family: monospace; text-shadow: 1px 1px 0px black; color: white; flex: 5; position: relative; } .timer div { position: absolute; background-color: rgba(255,255,255,0.2); left: 0; top: 0; width: 0; height: 38px; z-index: 2; } .timer span { position: absolute; z-index: 3; left: 19px; }
.timer
<div>
设为flex:5,这样它占据了控件栏的大部分宽度。 我们还设置 {{cssxref("position")}}: relative
,这样我们就可以根据它的边界方便地定位元素,而不是{{htmlelement("body")}} 元素的边界。 <div>
position:absolute
绝对定位于外部 <div>
的顶部。 它的初始宽度为0,因此根本无法看到它。随着视频的播放,JavaScript将动态的增加其宽度。<span>
也绝对位于计时器/进度条 timer
栏的左侧附近。<div>
和 <span>
定义适当数值的 {{cssxref("z-index")}} ,以便进度条显示在最上层,内部 <div>
显示在下层。 这样,我们确保我们可以看到所有信息 —— 一个box不会遮挡另一个。我们已经有了一个相当完整的 HTML 和CSS 接口;现在我们只需要调通所有按钮以使控件正常工作。
在与 index.html 文件相同的目录下创建新的JavaScript文件。命名为 custom-player.js
。
在此文件的顶部,插入以下代码:
var media = document.querySelector('video'); var controls = document.querySelector('.controls'); var play = document.querySelector('.play'); var stop = document.querySelector('.stop'); var rwd = document.querySelector('.rwd'); var fwd = document.querySelector('.fwd'); var timerWrapper = document.querySelector('.timer'); var timer = document.querySelector('.timer span'); var timerBar = document.querySelector('.timer div');
这里我们创建变量来保存对我们想要操作的所有对象的引用。有如下三组:
<video>
元素,和控制栏。<div>
,数字计时器的 <span>
,以及内部的 <div>
会随着视频播放逐渐变宽。接下来,在代码的底部插入以下内容:
media.removeAttribute('controls'); controls.style.visibility = 'visible';
这两行从视频中删除默认浏览器控件,并使自定义控件可见。
让我们实现或许是最重要的控制——播放/暂停按钮。
首先,将以下内容添加到您代码的底部,以便于在单击播放按钮时调用 playPauseMedia()
函数:
play.addEventListener('click', playPauseMedia);
现在定义 playPauseMedia()
函数——再次添加以下内容到您代码底部:
function playPauseMedia() { if(media.paused) { play.setAttribute('data-icon','u'); media.play(); } else { play.setAttribute('data-icon','P'); media.pause(); } }
我们使用if语句来检查视频是否暂停。如果视频已暂停,{{domxref("HTMLMediaElement.paused")}} 属性将返回 true,任何视频没有播放的时间,包括第一次加载时处于0的时间段都是视频暂停状态。如果已暂停,我们把play按钮的 data-icon
属性值设置成"u", 用以表示 "暂停" 按钮图标,并且调用{{domxref("HTMLMediaElement.play()")}} 函数播放视频。
点击第二次,按钮将会切换回去——"播放"按钮图标将会再次显示,并且视频将会被{{domxref("HTMLMediaElement.paused()")}} 函数暂停。
接下来,让我们添加处理视频停止的方法。添加以下的 addEventListener()
行在你之前添加的内容的下面:
stop.addEventListener('click', stopMedia); media.addEventListener('ended', stopMedia);
{{event("click")}} 事件很明显——我们想要在点击停止按钮的时候停止视频通过运行我们的 stopMedia()
函数。然而我们也希望停止视频当视频播放完成时——由{{event("ended")}} 事件标记,所以我们也会设置一个监听器在此事件触发时运行函数。
接下来,让我们定义 stopMedia()
—— 在 playPauseMedia() 后面
添加以下函数:
function stopMedia() { media.pause(); media.currentTime = 0; play.setAttribute('data-icon','P'); }
在 HTMLMediaElement API 中没有 stop()
方法——等效的办法是先用 pause()
暂停视频,然后设置{{domxref("HTMLMediaElement.currentTime","currentTime")}} 属性为0。设置 currentTime
的值(单位:秒)将会立刻使视频跳到该位置。
之后要做的事是把显示的图标设置成“播放”图标。无论视频使暂停还是正在播放,您都希望它随后可以播放。
有许多方法可以实现快退和快进功能;在这里,我们向您展示了一种相对复杂的方式,当按意外顺序按下不同的按钮时,它不会中断。
首先,在前面的代码之下添加以下两个addEventListener()
:
rwd.addEventListener('click', mediaBackward); fwd.addEventListener('click', mediaForward);
现在转到事件处理函数 - 在以前的函数下面添加以下代码来定义mediaBackward()
和mediaForward()
:
var intervalFwd; var intervalRwd; function mediaBackward() { clearInterval(intervalFwd); fwd.classList.remove('active'); if(rwd.classList.contains('active')) { rwd.classList.remove('active'); clearInterval(intervalRwd); media.play(); } else { rwd.classList.add('active'); media.pause(); intervalRwd = setInterval(windBackward, 200); } } function mediaForward() { clearInterval(intervalRwd); rwd.classList.remove('active'); if(fwd.classList.contains('active')) { fwd.classList.remove('active'); clearInterval(intervalFwd); media.play(); } else { fwd.classList.add('active'); media.pause(); intervalFwd = setInterval(windForward, 200); } }
您会注意到,首先我们初始化两个变量 - intervalFwd和intervalRwd - 您将在后面发现它们的用途。
让我们逐步浏览mediaBackward()
(mediaForward()
的功能性完全相同,但效果相反):
fwd
(快进)按钮后再按下rwd
(快退)按钮,就可以取消任何快进的功能并将其替换为快退功能。如果我们试图同时做到这两点,播放器就会暂停。if
语句检查是否已在rwd
按钮上设置了用来指示它已被按下的active
类。{{domxref("classList")}}是一个非常方便的属性,存在于每个元素上 ––它包含元素上设置的所有类的列表,以及添加/删除类的方法等。使用classList.contains()
方法检查列表是否包含active
类。这将返回布尔值true/false
结果。rwd
按钮上设置了active
,我们使用classList.remove()
删除它,清除第一次按下按钮时设置的间隔(参见下面的更多解释),并使用{{domxref("HTMLMediaElement.play()")}}取消快退并开始正常播放视频。classList.add()
将active
类添加到rwd
按钮,使用{{domxref("HTMLMediaElement.pause()")}}暂停视频,然后设置intervalRwd
变量为{{domxref("WindowOrWorkerGlobalScope.setInterval", "setInterval()")}}的调用。调用时,setInterval()
会创建一个活动间隔,这意味着它每隔x毫秒运行一个作为第一个参数给出的函数,其中x是第二个参数的值。 所以这里我们每200毫秒运行一次windBackward()
函数 ––我们将使用此函数不断向后滚动(快退动作)视频。要停止{{domxref("WindowOrWorkerGlobalScope.setInterval", "setInterval()")}}运行,你必须调用{{domxref("WindowOrWorkerGlobalScope.clearInterval", "clearInterval()")}},给它识别要清除的间隔的名称,在本例中是变量名称intervalRwd(请参阅函数中较早的clearInterval()调用)。最后,对于本节,定义在setInterval()调用中需要调用的windBackward()和windForward()函数。在以上两个函数下面添加以下内容:
function windBackward() { if(media.currentTime <= 3) { rwd.classList.remove('active'); clearInterval(intervalRwd); stopMedia(); } else { media.currentTime -= 3; } } function windForward() { if(media.currentTime >= media.duration - 3) { fwd.classList.remove('active'); clearInterval(intervalFwd); stopMedia(); } else { media.currentTime += 3; } }
同样,我们将完成这些功能中的第一个,因为它们几乎完全相同,但彼此相反。在windBackward()
中,我们执行以下操作 ––请记住,当间隔处于活动状态时,此函数每200毫秒运行一次。
if
语句开始,该语句检查当前时间是否小于3秒,即,如果再倒退三秒将使其超过视频的开始。这会导致奇怪的行为,所以如果是这种情况,我们通过调用stopMedia()
来停止视频播放,从倒带按钮中删除active
类,并清除intervalRwd
间隔以停止快退功能。如果我们没有做到最后一步,视频将永远保持快退。media.currentTime-=3
从当前时间中删除三秒。因此,实际上,我们将视频快退3秒,每200毫秒一次。我们要实施的媒体播放器的最后一块是显示的时间。为此,我们将运行一个函数,以便每次在<video>元素上触发 {{event("timeupdate")}}事件时更新时间显示。此事件触发的频率取决于您的浏览器,CPU电源等(see this stackoverflow post)。
在代码下方添加addEventListener()
行:
media.addEventListener('timeupdate', setTime);
现在定义setTime()
函数。在文件底部添加以下内容:
function setTime() { var minutes = Math.floor(media.currentTime / 60); var seconds = Math.floor(media.currentTime - minutes * 60); var minuteValue; var secondValue; if (minutes < 10) { minuteValue = '0' + minutes; } else { minuteValue = minutes; } if (seconds < 10) { secondValue = '0' + seconds; } else { secondValue = seconds; } var mediaTime = minuteValue + ':' + secondValue; timer.textContent = mediaTime; var barLength = timerWrapper.clientWidth * (media.currentTime/media.duration); timerBar.style.width = barLength + 'px'; }
这是一个相当长的函数,所以让我们一步一步地完成它:
minuteValue
和secondValue
。if
语句计算出分钟数和秒数是否小于10.如果是这样,它们会在类似数值时钟显示工作的方式上为值添加前置的零。minuteValue
加上冒号字符加secondValue
。<div>
的长度是通过首先计算外部<div>
的宽度来计算出来的(任何元素的{{domxref("HTMLElement.clientWidth", "clientWidth")}} 属性将包含它的长度),然后乘以{{domxref("HTMLMediaElement.currentTime")}}除以媒体的总{{domxref("HTMLMediaElement.duration")}}。<div>
的宽度设置为等于计算的条形长度加上“px”,因此它将设置为该像素数还有一个问题需要修复。如果在快退或快进功能激活时按下播放/暂停或停止按钮,它们就不起作用。我们如何修复它以便取消rwd / fwd
按钮功能并按照您的预期播放/停止视频?这很容易解决。
首先,在stopMedia()
函数中添加以下行 ––任何地方都可以:
rwd.classList.remove('active'); fwd.classList.remove('active'); clearInterval(intervalRwd); clearInterval(intervalFwd);
现在再次添加相同的行(上面的四行)到playPauseMedia()
函数的最开头(就在if
语句的开始之前)。
此时,您可以删除windBackward()
和windForward()
函数中的等效行,因为该函数已在stopMedia()
函数中实现。
注意:您还可以通过创建运行这些行的单独函数来进一步提高代码的效率,然后在需要的任何地方调用它,而不是在代码中多次重复这些行。但是我们会把这些留给你自己。
我想我们已经在这篇文章中教过你足够多了。使用{{domxref("HTMLMediaElement")}} API可以为创建简单的视频和音频播放器提供丰富的可用功能,然而这只是冰山一角。 有关更复杂和有趣功能的链接,请参阅下面的“另请参阅”部分。
以下是一些有关如何增强我们构建的现有示例的建议:
由于 <audio>
元素具有相同的{{domxref("HTMLMediaElement")}}功能,因此您可以轻松地将此播放器用于 <audio>
元素。 试着这样做。
你能找到一种方法将计时器内部的 <div>
元素转换为真正的搜索条/ 滑动条- 也就是说,当你点击条形图上的某个位置时,它会跳转到视频播放中的相对位置吗? 作为提示,您可以通过getBoundingClientRect()
方法找出元素左/右和上/下侧的X和Y值 , 而且你可以通过 {{domxref("Document")}} 对象调用的click事件的事件对象找到鼠标单击的坐标。举个栗子:
document.onclick = function(e) { console.log(e.x) + ',' + console.log(e.y) }
Media formats supported by the HTML audio and video elements.
{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Drawing_graphics", "Learn/JavaScript/Client-side_web_APIs/Client-side_storage", "Learn/JavaScript/Client-side_web_APIs")}}