怎么用 JavaScript 构建自定义的 HTML5 视频播放器 您所在的位置:网站首页 svg图标需要用插件才能显示嘛 怎么用 JavaScript 构建自定义的 HTML5 视频播放器

怎么用 JavaScript 构建自定义的 HTML5 视频播放器

2023-05-14 00:54| 来源: 网络整理| 查看: 265

在网页中观看和分享视频内容是一个很常见的功能,多年来,视频嵌入网页的方式发生了变化。现在,我们在现代浏览器中使用 标签就可以添加视频文件到网页上,该标签支持多个视频格式。

video_element.webp

当使用 标签时的主要警告是渲染的视频播放器会因浏览器而异,如果你想提供一致的用户体验,使用原生操作并不理想。这就是为什么构建自定义控件而不是使用浏览器默认界面很有用的原因。

在这个教程中,我将会带你使用 JavaScript 构建一个自定义的视频播放器。目标是如何利用浏览器 HTML5 Media API 来提升默认设置的体验。

我们将在本教程中构建一个看起来像 YouTube 视频播放器,因为我认为复制大多数人已经熟悉的一些功能是个好主意。

当然,我们并不会实现 YouTube 播放器上的所有功能,因为这会让教程更长、更复杂。然而,一旦你完成了本教程,我相信你能够很轻松地加入新的功能。

你可以查看我们将构建的线上案例,或者在 GitHub 上查看源码。

准备条件

你需要对 JavaScript 和 DOM 有基本的了解,才能继续学习本教程。我推荐你使用最新版本的谷歌浏览器,因为在本文编写时,我们将添加的一些功能(比如画中画功能)仅适用于谷歌(Webkit 内核)浏览器。

开始

我在 GitHub 中为本教程准备了开始文件。有需要的话,你可以克隆到自己的机器上,并在编辑器中打开。你将分别在 index.html 和 style.css 中找到播放器的标记文档文件及其样式,以及我们用来测试播放器的视频文件。index.js 将是我们添加播放器工作所需的所有 JavaScript 代码的地方。

在终端中运行 npm install 来安装 browser-sync 作为启动 Web 服务器的开发依赖项,其在任何文件更改时自动刷新页面。然后 npm start 启动项目,监听浏览器 http://localhost:3000。

目前都做了些什么

现在,视频播放器保留本机浏览器控件,正如你所期待那样工作。自定义控件已经被定义在 #video-controls 元素,但是它们被隐藏了。

. . . . . . 复制代码

即使我们要为控件实现自定义界面,保留 元素上的 controls 属性是个很好的主意,这样用户不管出于什么原因禁用 JavaScript,浏览器本机的控件依旧可使用。对于其他人,本机空间可以轻松隐藏并替换成自定义控件,这稍后进行演示。

海报图像已经添加到视频中,设置 preload属性值为 metadata,这指示浏览器仅获取视频元数据(比如 duration)。为了让事情简单点,我们只添加 MP4 类型的视频源文件,因为该类型的视频被所有主流浏览器兼容,是一个非常安全的默认值。有关视频格式和浏览器兼容性的更多信息,可参考该文档。

. . . . . . 复制代码 隐藏自带控件

我们首先需要做的事情是在确认浏览器支持 HTML5 视频后,隐藏默认视频控件并提供我们自己的界面。在你的 index.js 文件中输入下面代码片段来实现上面的功能:

// index.js // Select elements here const video = document.getElementById('video'); const videoControls = document.getElementById('video-controls'); const videoWorks = !!document.createElement('video').canPlayType; if (videoWorks) { video.controls = false; videoControls.classList.remove('hidden'); } 复制代码

canPlayType 属性是我们检查浏览器对视频格式支持的方式。要使用它,我们需要创建 元素的实例并将检查是否支持 canPlayType。如果支持,则可以安全地假设其支持 HTML 视频,然后禁用默认控件,启用我们自定义的控件。

canPlayType_support.webp

默认控件已经被替换成自定义控件

切换播放状态

让我们从基础开始。我们需要通过点击播放按钮来播放或者暂停视频,并且更改应该匹配视频状态的图标。我们从获取视频和播放按钮开始,代码在 index.js 顶部,如下:

// index.js const playButton = document.getElementById('play'); 复制代码

然后,我们创建一个函数来切换视频播放状态:

// index.js // Add functions here // togglePlay toggles the playback state of the video. // If the video playback is paused or ended, the video is played // 如果视频播放是暂停或者视频结尾状态,视频播放 // otherwise, the video is paused // 否则,视频暂停 function togglePlay() { if (video.paused || video.ended) { video.play(); } else { video.pause(); } } 复制代码

最后,我们创建一个时间监听器,当 playButton 按钮被点击后执行 togglePlay 方法。

// index.js // Add eventlisteners here playButton.addEventListener('click', togglePlay); 复制代码

够简单吧?通过点击浏览器中的播放按钮对其测试。它应该正确地播放和暂停视频。

appropriately_play_and_stop_video.webm.gif

这实际上为本教程的其他部分定下了基调。我们通常会选择一个视频控件,创建一个实现特定功能的函数,通过事件监听器将其连接起来。

我们继续,根据视频状态更新播放按钮。下面是 playButton 的 HTML 文件:

. . . . . . 复制代码

在 元素中,我们有播放和暂停按钮,但是一次我们只能展示其中一个,另一个则隐藏。现在我们要做的就是切换每个图标的 hidden 类,以便根据视频的状态展示正确的图标。

首先,在 index.js 文件顶部选择图标:

// index.js const playbackIcons = document.querySelectorAll('.playback-icons use'); 复制代码

接着,在 togglePlay 函数下创建一个函数,用来更新播放按钮:

// index.js // updatePlayButton updates the playback icon and tooltip // depending on the playback state // 根据播放状态,updatePlayButton 函数更新播放图标和提示 function updatePlayButton() { playbackIcons.forEach(icon => icon.classList.toggle('hidden')); } 复制代码

最后,在文件底部添加如下事件监听器:

// index.js video.addEventListener('play', updatePlayButton); video.addEventListener('pause', updatePlayButton); 复制代码

当视频播放或者暂停时,updatePlayButton 函数都会被执行,切换每个按钮中的 hidden 类。因为暂停按钮元素默认值是 hidden 类,一旦视频被播放,这个暂停图标出现,播放图标将会隐藏。如果视频被暂停,则会发生相反的情况。你可以在自己浏览器上测试。

额外要做的事情是,当鼠标移动到播放按钮上,需要更新展示的提示文本。默认提示是 play(k),但是当视频正在播放,需要更新提示信息为 pause(k)。k 是我们将在本教程后面添加播放或者暂停视频的键盘快捷键。

如下,更新 updatePlayButton 函数:

// index.js function updatePlayButton() { playbackIcons.forEach(icon => icon.classList.toggle('hidden')); if (video.paused) { playButton.setAttribute('data-title', 'Play (k)') } else { playButton.setAttribute('data-title', 'Pause (k)') } } 复制代码

当视频正在播放或者暂停时,鼠标移动到按钮上,应该设置正确的提示文本。

如果你想知道提示信息是怎么展示的,可以看下相关的 CSS:

// style.css . . . button::before { content: attr(data-title); position: absolute; display: none; right: 0; top: -50px; background-color: rgba(0, 0, 0, 0.6); color: #fff; font-weight: bold; padding: 4px 6px; word-break: keep-all; white-space: pre; } button:hover::before { display: inline-block; } . . . 复制代码

video-tooltip.gif

展示视频持续时间和经过时间

展示视频时长很必要,因为这是用户首先想看到的,所以我们接下来将讲解。

下面是持续时长和经过时间的元素标记:

00:00 / 00:00 复制代码

通过 index.js 选择这两个控件(元素),如下:

// index.js const timeElapsed = document.getElementById('time-elapsed'); const duration = document.getElementById('duration'); 复制代码

一旦页面加载完成后,我们将使用 duration 属性展示视频的总时长。这个属性表示的是视频的总秒数,所以在展示之前,我们需要将其转换成分秒。我们创建一个 formatTime 函数,将时间转换成分秒:

// index.js // formatTime takes a time length in seconds and returns the time in // minutes and seconds function formatTime(timeInSeconds) { const result = new Date(timeInSeconds * 1000).toISOString().substr(11, 8); return { minutes: result.substr(3, 2), seconds: result.substr(6, 2), }; }; 复制代码

接着,我们在 formatTime 函数下创建 initializeVideo 函数:

// index.js // initializeVideo sets the video duration, and maximum value of the // progressBar function initializeVideo() { const videoDuration = Math.round(video.duration); const time = formatTime(videoDuration); duration.innerText = `${time.minutes}:${time.seconds}`; duration.setAttribute('datetime', `${time.minutes}m ${time.seconds}s`) } 复制代码

如上所示,视频持续时长被四舍五入,格式化为分秒,然后在屏幕上更新。datetime 同步更新为时间字符串,表示视频持续时长。

接着,如下所示,让我们将 initializeVideo 函数连接到 loadedmetadata 监听器上。当元数据被加载之后,将会更新视频的持续时长。

// index.js video.addEventListener('loadedmetadata', initializeVideo); 复制代码

video-duration.webp

同理,当视频播放过程中,我们更新播放经过的时间。下面的函数能帮我们实现这个功能:

// index.js // updateTimeElapsed indicates how far through the video // the current playback is function updateTimeElapsed() { const time = formatTime(Math.round(video.currentTime)); timeElapsed.innerText = `${time.minutes}:${time.seconds}`; timeElapsed.setAttribute('datetime', `${time.minutes}m ${time.seconds}s`) } 复制代码

我们需要 timeupdate 事件监听视频。无论什么时候,视频的 currentTime 属性值更新了,事件就会触发。

// index.js video.addEventListener('timeupdate', updateTimeElapsed); 复制代码

上面的代码确保视频的 currentTime 更新,经过时间也会适当更新。

current-time-update.gif

更新进度条

接下来我们要做的事情是当视频播放,更新进度条。下面是进度条的元素标志:

. . . 00:00 . . . 复制代码

上面,我们有 progress 元素,用于显示任务的进度条,而 range 类型的 input 允许我们快速无缝浏览视频。两个元素我都用同个样式修饰,所以它们有一样的宽高,但是 input 是透明色(除了与进度条内相同的颜色的指示点)。

如果你很好奇,你可以仔细看 CSS 的内容,看看我是怎么做的。让进度条看起来像一个单一的元素是一种 hack,但是我觉得对我们的用例来说很合理。

两者的 min 属性被设置为 0,两者的 value 属性指向当前时间值。它们还需要一个 max 属性,该属性将设置为视频的持续时间(以秒为单位),该属性值来自 video.duration,如上所示。我们可以在 initializeVideo 函数中实现,但是我们得先选择元素:

// index.js const progressBar = document.getElementById('progress-bar'); const seek = document.getElementById('seek'); 复制代码

然后如下更新 initializeVideo 函数:

// index.js function initializeVideo() { const videoDuration = Math.round(video.duration); seek.setAttribute('max', videoDuration); progressBar.setAttribute('max', videoDuration); const time = formatTime(videoDuration); duration.innerText = `${time.minutes}:${time.seconds}`; duration.setAttribute('datetime', `${time.minutes}m ${time.seconds}s`) } 复制代码

现在,进度条元素的范围输入在 0 和以秒为单位的视频持续时长之间,如属性 min 和 max 属性。正如你将看到的,这使得我们能够在任何时间点轻松地将进度条和时间范围同步。

继续,当视频被播放我们就更新上述元素的值,以便进度条发挥作用。如下,创建 updateProgress 函数:

// index.js // updateProgress indicates how far through the video // the current playback is by updating the progress bar function updateProgress() { seek.value = Math.floor(video.currentTime); progressBar.value = Math.floor(video.currentTime); } 复制代码

然后,在第一个事件监听器下,为 video 添加一个新的名为 timeupdate 事件监听器:

// index.js video.addEventListener('timeupdate', updateProgress); 复制代码

刷新你的浏览器,然后尝试。当视频被播放,你应该看到进度条更新。

progress-bar-update.gif

预先跳转

大多数的播放器都允许你点击进度条跳转到视频指定的点,我们的视频播放器也将一样。首先,我们需要选择提示信息元素:

// index.js const seekTooltip = document.getElementById('seek-tooltip'); 复制代码

然后,添加一个函数,用来当光标移动到进度条上在信息元素里展示时间戳:

// index.js // updateSeekTooltip uses the position of the mouse on the progress bar to // roughly work out what point in the video the user will skip to if // the progress bar is clicked at that point function updateSeekTooltip(event) { const skipTo = Math.round((event.offsetX / event.target.clientWidth) * parseInt(event.target.getAttribute('max'), 10)); seek.setAttribute('data-seek', skipTo) const t = formatTime(skipTo); seekTooltip.textContent = `${t.minutes}:${t.seconds}`; const rect = video.getBoundingClientRect(); seekTooltip.style.left = `${event.pageX - rect.left}px`; } 复制代码

此函数在 seek 元素中,使用光标位置粗略计算用户悬停范围输入框的地方,然后将位置信息存放在 data-seek 属性中,同时更新提示信息以反映该位置的时间戳。

在 seek 控制器中关联 updateSeekTooltip 函数和 mousemove 来查看效果:

// index.js seek.addEventListener('mousemove', updateSeekTooltip); 复制代码

mousemove-tooltip.gif

不管是点击或者拖拽指示点,一旦 seek 元素值发生更改,我们希望跳转到 data-seek 属性设置的时间点。

在 updateSeekTooltip 函数下,创建一个新的名为 skipAhead 的函数:

// index.js // skipAhead jumps to a different point in the video when // the progress bar is clicked function skipAhead(event) { const skipTo = event.target.dataset.seek ? event.target.dataset.seek : event.target.value; video.currentTime = skipTo; progressBar.value = skipTo; seek.value = skipTo; } 复制代码

使用 input 事件监控 seek 元素发生更改时,将执行此函数。然后,我们获取 data-seek 的值并检查其是否有效。如果有效,我们获取该值并更新视频播放过的时间和进度条的位置。如果 data-seek 属性不存在(比如在手机端),改为使用 seek 元素的值。

这产生跳转到视频指定位置的效果。

// index.js seek.addEventListener('input', skipAhead); 复制代码

jump-ahead-demo.gif

音频控制 . . . . . . 复制代码

在上面代码片段中,你可以找到所有相关音频控件的标记。我们有一个按钮,根据视频音频的状态展示,和一个控制音频范围的 input 元素。

首先,当 #volume 元素的值发生更改,我们要做的就是更改视频的音频大小。我们也要更新视频当前的图标。

正如你所见,音频的输入范围是 0 到 1,并以 0.01 的值递增。以这种方式设置它是为了使其与视频的音量属性值保持一致,该属性值的范围也是从 0 到 1,其中 0 是最低音量,1 是最高音量。

继续,我们选择按钮,图标和输入框,如下 index.js 所示:

// index.js const volumeButton = document.getElementById('volume-button'); const volumeIcons = document.querySelectorAll('.volume-button use'); const volumeMute = document.querySelector('use[href="#volume-mute"]'); const volumeLow = document.querySelector('use[href="#volume-low"]'); const volumeHigh = document.querySelector('use[href="#volume-high"]'); const volume = document.getElementById('volume'); 复制代码

接着,创建一个新的名为 updateVolume 函数,当音频输入框值发生更改,该函数更新视频音频值:

// index.js // updateVolume updates the video's volume // and disables the muted state if active function updateVolume() { if (video.muted) { video.muted = false; } video.volume = volume.value; } 复制代码

然后,将其和 volume 元素关联起来,如下:

// index.js volume.addEventListener('input', updateVolume); 复制代码

到这里,你将意识到当你左滑输入框时,音量减少,反之音量增加。我们需要添加另一个函数来在音量变化时更新图标:

// index.js // updateVolumeIcon updates the volume icon so that it correctly reflects // the volume of the video function updateVolumeIcon() { volumeIcons.forEach(icon => { icon.classList.add('hidden'); }); volumeButton.setAttribute('data-title', 'Mute (m)') if (video.muted || video.volume === 0) { volumeMute.classList.remove('hidden'); volumeButton.setAttribute('data-title', 'Unmute (m)') } else if (video.volume > 0 && video.volume


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有