JavaScript 30天编码挑战——第 11 天

230 阅读1分钟

这是我参与更文挑战的第13天,活动详情查看: 更文挑战

Custom HTML5 Video Player

这个模块一看标题就知道了,是要实现一个自定义的 HTML5 视频播放器,根据原作者的最终代码展示,可以看出,最终的效果里要实现视频的进度条可以控制,点击画面可以进行暂停/播放,左边的控制条为声音控制,右边的控制条为倍速控制。

一、效果展示

1.index-START.html

就是一个样式效果,先替你实现好了,默认运行就是这样效果。 1.gif

2.index-FINISHED.html

将代码中的默认 JS 文件换成 带后缀完成标识的 JS 文件。 2-min.gif

二、实现

最终代码

    /* 获取元素 */
const player = document.querySelector('.player');
const video = player.querySelector('.viewer');
const progress = player.querySelector('.progress');
const progressBar = player.querySelector('.progress__filled');
const toggle = player.querySelector('.toggle');
const skipButtons = player.querySelectorAll('.skip');
const ranges = player.querySelectorAll('.player__slider');


function togglePlay() {
  const method = video.paused ? 'play' : 'pause';
  video[method]();
}

function updateButton() {
  const icon = this.paused ? '►' : '❚ ❚';
  console.log(icon);
  toggle.textContent = icon;
}

function skip() {
  video.currentTime += parseFloat(this.dataset.skip);
 }

 function handleRangeUpdate() {
   //volume, playbackRate
  video[this.name] = this.value;
}

function handleProgress() {
  //currentTime 当前时间
  //duration 总时间
  //flexBasis 预设宽度
  const percent = (video.currentTime / video.duration) * 100;
  progressBar.style.flexBasis = `${percent}%`;
}

function scrub(e) {
  const scrubTime = (e.offsetX / progress.offsetWidth) * video.duration;
  video.currentTime = scrubTime;
}


/* Hook up the event listeners */
video.addEventListener('click', togglePlay);
toggle.addEventListener('click', togglePlay);

video.addEventListener('play', updateButton);
video.addEventListener('pause', updateButton);

video.addEventListener('timeupdate', handleProgress);

skipButtons.forEach(button => button.addEventListener('click', skip));

ranges.forEach(range => range.addEventListener('change', handleRangeUpdate));
ranges.forEach(range => range.addEventListener('mousemove', handleRangeUpdate));


let mousedown = false;
progress.addEventListener('click', scrub);
progress.addEventListener('mousemove', (e) => mousedown && scrub(e));
progress.addEventListener('mousedown', () => mousedown = true);
progress.addEventListener('mouseup', () => mousedown = false);

三、总结回顾

这个模块属于一个比较轻松的模块。

首先我们 video 标签那里加一个 controls 的属性,这时候我们就会发现,其实我们这个属性已经自带了一些控制的页面属性。

<video class="player__video viewer" src="652333414.mp4" controls></video>

image.png

当然原作者的效果更偏向于对于知识点和技术的练习,各自甄选进行学习。

原作者的这部分内容写的已经很好了,主要以分析理解为主,再复写一遍没有太大的意义。

过程分解

  1. 获取元素
/*进行了一些微调,主要看个人使用习惯*/
 <button data-skip="-10" class="player__button skip">« 10s</button>
 <button data-skip="25" class="player__button skip">25s »</button>

/* 获取元素 */
const player = document.querySelector('.player');
const video = player.querySelector('.viewer');
const progress = player.querySelector('.progress');
const progressBar = player.querySelector('.progress__filled');
const toggle = player.querySelector('.toggle');
const skipButtons = player.querySelectorAll('.skip');
const ranges = player.querySelectorAll('.player__slider');
  1. 相关事件 (1)播放/暂停
function togglePlay() {
  const method = video.paused ? 'play' : 'pause';
  video[method]();
}

这里使用了三元运算符,就省去了 if...else 这样比较冗杂的写法。

第二个比较有意思比较新颖的是 video[method]() 这样的写法。

(2)播放/暂停时,按钮的改变

function updateButton() {
  const icon = this.paused ? '►' : '❚ ❚';
  console.log(icon);
  toggle.textContent = icon;
}

但是这样我就产生了疑问,之前要是类似这种的,肯定就在一个事件里去写了,如果拆分成两个事件的话,那么怎么串起来呢?

(3)事件串联

/* Hook up the event listeners */

video.addEventListener('click', togglePlay);
toggle.addEventListener('click', togglePlay);

video.addEventListener('play', updateButton);
video.addEventListener('pause', updateButton);

不得不说,我对这种写法感觉比较新奇。他是这样做的,当你点击的时候,会调用 togglePlay() 的 play() 和 pause() 方法,顺便在调用这两个方法的时候去改变按钮的切换。

哇,学到了学到了!!

(4)画面前进/后退

 function skip() {
   video.currentTime += parseFloat(this.dataset.skip);
 }
 
 
 skipButtons.forEach(button => button.addEventListener('click', skip));

(5)音量和播放速度的事件绑定

function handleRangeUpdate() {
//volume, playbackRate
  video[this.name] = this.value;
}


ranges.forEach(range => range.addEventListener('change', handleRangeUpdate));
ranges.forEach(range => range.addEventListener('mousemove', handleRangeUpdate));

以前可能会用 video.volume 和 video.playbackRate 去取值,现在使用中括号就可以直接实现,省去了很多的步骤。 (6)画面拖动播放

function handleProgress() {
  //currentTime 当前时间
  //duration 总时间
  //flexBasis 预设宽度
  const percent = (video.currentTime / video.duration) * 100;
  progressBar.style.flexBasis = `${percent}%`;
}

(7)画面点击播放对应时间

function scrub(e) {
  const scrubTime = (e.offsetX / progress.offsetWidth) * video.duration;
  video.currentTime = scrubTime;
}


let mousedown = false;
progress.addEventListener('click', scrub);
progress.addEventListener('mousemove', (e) => mousedown && scrub(e));
progress.addEventListener('mousedown', () => mousedown = true);
progress.addEventListener('mouseup', () => mousedown = false);

最后不要忘记了进度条的更新!!!

video.addEventListener('timeupdate', handleProgress);

不然就会是这个样子!!

3.gif

四、重难点

首先自动检索相关学习知识点:

developer.mozilla.org/zh-CN/docs/…

developer.mozilla.org/zh-CN/docs/…

其实如果吸收了这两个部分的内容,今天这个模块理解起来就非常容易。

此外就是原作者在里边增加了一些比较新奇的写法,对于编程的思路和风格都是一个非常好的引导的指向标。