接上一篇:HTML5 媒体播放器 video、audio 自定义播放器进度条(上)
本篇主要介绍进度条与播放器标签的交互,以 <video> 举例,<audio>基本一样。
1、结构
代码添加如下结构:
<div class="box">
<div class="media-box">
<video
id="videoId"
class="video"
width="100%"
height="100%"
controls></video>
</div>
<div id="slide-box">
<div id="slide">
<div id="slide-progress"></div>
<div id="slide-inside"></div>
<div id="point-box">
<div class="point"></div>
</div>
</div>
</div>
<button id="btn">开始</button>
</div>
2、相关事件
- 通过监听<video>相关事件,动态更新进度条
2.1、定义变量并监听事件
监听播放器主要事件之前,先定义一下变量:
let duration = '' // 用于显示的总时长,格式 xx:xx
let currentTime = '' // 用于显示的当前播放的时刻
let max = '' // 总时长
let value = '' // 当前时刻
let video = null
let videoDom = document.getElementById('videoId')
let src = ''
videoDom.addEventListener('canplay', canplay)
videoDom.addEventListener('timeupdate', timeupdate)
videoDom.addEventListener('pause', videoPause)
videoDom.addEventListener('ended', videoEnded)
videoDom.addEventListener('error', videoError)
videoDom.addEventListener('progress', videoProgress)
关于 <vidoe> 简介,可参考:HTML5 <video>常用属性、时间、方法及基础使用说明
2.2 canplay--媒体可播放
function canplay(e) {
let v = e.target;
duration = formatterTime(v.duration);
max = parseInt(v.duration * 1000); //目的是让进度条计算时精确,也可以不*1000
video = v;
console.log('canplay',max);
}
该事件主要通过 duration 获取到总时长,
formatterTime 函数用于格式化展示的数据:
function formatterTime(time) {
let minute = parseInt(time / 60);
let seconds = parseInt(time - minute * 60);
minute = minute < 10 ? "0" + minute : minute;
seconds = seconds < 10 ? "0" + seconds : seconds;
return `${minute}:${seconds}`;
}
2.3 timeupdate--播放时间更新
function timeupdate(e) {
currentTime = formatterTime(e.target.currentTime);
value = parseInt(e.target.currentTime * 1000);
console.log('timeupdate',e.target.currentTime,max,`${currentTime}:${duration}`);
// 更新进度条
slideProcess(value)
}
该事件主要检测视频播放时间更新,动态更新进度条
其中 slideProcess 计算:
function slideProcess(v) {
v = v >= max ? max : v
slideInsideDom.style.width = (v / max) * 100 + '%'
pointBoxDom.style.left = (v / max) * 100 + '%'
}
2.4 progress -- 更新 加载进度
function videoProgress(e) {
if( e.target.buffered.length > 0 ){
// console.log('videoProgress-加载进度', e.target.buffered.end(0) );
let v = e.target.buffered.end(0) * 1000
progressDom.style.width = ( v / max) * 100 + '%'
}
}
该事件主要用于更新加载进度
2.5 播放
定义一按钮,实现简单点击播放,到这里,进度条就可以根据视频动态更新位置及显示加载条
let btnDom = document.getElementById('btn')
btnDom.addEventListener('click', btnClick)
function btnClick(e){
pauseEvent(e)
console.log('鼠标点击');
init('./mov_bbb.mp4')
}
function init(s) {
src = s
if(!video)video = videoDom
video.src = src;
video.play()
.then(() => {
})
.catch(err => {
console.error(err);
videoPause();
});
}
3、进度条操作
进度条的操作主要是监听鼠标按下 mousemove 和移动拖拽事件 mousemove,当按下或拖拽时,进行的操作:
- 更新进度条样式
- 更新 video 的 currentTime
在 setPosition 函数添加:
function setPosition(e) {
let x = e.clientX - slideDom.offsetLeft
x = x < 0 ? 0 : x
x = x >= extent ? extent : x
slideInsideDom.style.width = (x / extent) * 100 + '%'
pointBoxDom.style.left = (x / extent) * 100 + '%'
// 新增如下代码
if(video && max){
video.currentTime = x / extent * max / 1000
}
}
一个简单的自定义进度条就实现了。
4、完整代码
可以将下面完整代码复制到本地运行。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.box {
width: 800px;
margin: 50px auto;
}
.media-box {
width: 800px;
height: 400px;
border: 1px solid #000;
}
#slide-box {
margin-top: 20px;
width: 700px;
height: 26px;
padding: 8px 0;
cursor: pointer;
}
#slide {
height: 10px;
position: relative;
background-color: #e4e7ed;
border-radius: 8px;
}
#slide-progress,
#slide-inside {
position: absolute;
top: 0;
left: 0;
height: 100%;
border-radius: 8px;
}
#slide-progress {
background-color: #c6c8c9;
width: 0%;
transition: all ease;
}
#slide-inside {
background-color: #f98e2d;
z-index: 10;
width: 0%;
transition: all ease;
}
#point-box {
width: 24px;
height: 24px;
position: absolute;
top: 50%;
left: 0%;
transform: translate(-50%, -50%);
z-index: 11;
}
.point {
position: absolute;
border: 12px solid #fff;
box-shadow: 1px 1px 2px #454343;
border-radius: 50%;
}
.point:hover {
transform: scale(1.2);
}
</style>
</head>
<body>
<div class="box">
<div class="media-box">
<video
id="videoId"
class="video"
width="100%"
height="100%"
controls></video>
</div>
<div id="slide-box">
<div id="slide">
<div id="slide-progress"></div>
<div id="slide-inside"></div>
<div id="point-box">
<div class="point"></div>
</div>
</div>
</div>
<button id="btn">开始</button>
</div>
<script>
let duration = '' // 用于显示的总时长,格式 xx:xx
let currentTime = '' // 用于显示的当前播放的时刻
let max = '' // 总时长
let value = '' // 当前时刻
let video = null
let videoDom = document.getElementById('videoId')
let src = ''
let slideDom = document.getElementById('slide-box')
let slideInsideDom = document.getElementById('slide-inside')
let pointBoxDom = document.getElementById('point-box')
let progressDom = document.getElementById('slide-progress')
let extent = slideDom.getBoundingClientRect().width
console.log(111111, extent);
slideDom.addEventListener('mousedown', mousedownFun)
document.addEventListener('mouseup', mouseupFun)
videoDom.addEventListener('canplay', canplay)
videoDom.addEventListener('timeupdate', timeupdate)
videoDom.addEventListener('pause', videoPause)
videoDom.addEventListener('ended', videoEnded)
videoDom.addEventListener('error', videoError)
videoDom.addEventListener('progress', videoProgress)
let btnDom = document.getElementById('btn')
btnDom.addEventListener('click', btnClick)
function btnClick(e){
pauseEvent(e)
console.log('鼠标点击');
init('./mov_bbb.mp4')
}
function init(s) {
src = s
if(!video)video = videoDom
video.src = src;
video.play()
.then(() => {
})
.catch(err => {
console.error(err);
videoPause();
});
}
function canplay(e) {
let v = e.target;
duration = formatterTime(v.duration);
max = parseInt(v.duration * 1000);
video = v;
console.log('canplay',max);
}
function timeupdate(e) {
currentTime = formatterTime(e.target.currentTime);
value = parseInt(e.target.currentTime * 1000);
console.log('timeupdate',e.target.currentTime,max,`${currentTime}:${duration}`);
// 更新进度条
slideProcess(value)
}
function videoPause() {
if (video) {
video.pause();
}
}
function videoEnded() {
}
function videoError() {
if (src) {
videoPause();
}
}
function videoProgress(e) {
if( e.target.buffered.length > 0 ){
// console.log('videoProgress-加载进度', e.target.buffered.end(0) );
let v = e.target.buffered.end(0) * 1000
progressDom.style.width = ( v / max) * 100 + '%'
}
}
function slideProcess(v) {
// console.log('slideProcess',max);
v = v >= max ? max : v
slideInsideDom.style.width = (v / max) * 100 + '%'
pointBoxDom.style.left = (v / max) * 100 + '%'
}
function formatterTime(time) {
let minute = parseInt(time / 60);
let seconds = parseInt(time - minute * 60);
minute = minute < 10 ? "0" + minute : minute;
seconds = seconds < 10 ? "0" + seconds : seconds;
return `${minute}:${seconds}`;
}
// ----------------------- 进度条操作 -------------------------------
// 监听进度条鼠标按下
function mousedownFun(e) {
setPosition(e)
console.log('进度条鼠标按下', e.clientX, slideDom.offsetLeft);
document.addEventListener('mousemove', mousemoveFun)
}
// 鼠标按下监听全局移动
function mousemoveFun(e) {
e = e || window.event;
pauseEvent(e);
setPosition(e);
console.log('拖拽移动',);
}
// 全局监听鼠标抬起
function mouseupFun(e) {
console.log('鼠标抬起', e.clientX);
document.removeEventListener('mousemove', mousemoveFun)
}
function setPosition(e) {
let x = e.clientX - slideDom.offsetLeft
x = x < 0 ? 0 : x
x = x >= extent ? extent : x
slideInsideDom.style.width = (x / extent) * 100 + '%'
pointBoxDom.style.left = (x / extent) * 100 + '%'
// 新增如下代码
if(video && max){
video.currentTime = x / extent * max / 1000
}
}
//阻止事件冒泡
//不仅仅要stopPropagation,还要preventDefault
function pauseEvent(e) {
if (e.stopPropagation) e.stopPropagation();
if (e.preventDefault) e.preventDefault();
e.cancelBubble = true;
e.returnValue = false;
return false;
}
</script>
</body>
</html>