H5 浏览器自定义用户控件
3,4,---
title: H5 浏览器自定义用户控件的实现
date: 2017-08-01 14:09:00
categories: 技术
tags: 前端
参考文章:
https://css-tricks.com/custom-controls-in-html5-video-full-screen/
https://blog.hellojs.org/creating-a-custom-html5-video-player-and-the-shadow-dom-a98f29261be4
原生用户控件
对于 <video>
标签,有一个名为 'controls' 的属性,按如下写法就能给播放器增加原生的暂停/开始、进度条、音量、视频最大化这些基础功能。
<video id="myVideo" controls ></video>
自定义用户控件
但是原生控件往往无法满足我们的一些需求,所以自定义用户控件还是很必要的。
首先声明一个变量,我们把 <video id="video"></video>
做如下定义
let videoElement = document.getElementById('video');
let $videoElement = $('#video');
如何实现自定义控件,说起来也很简单,只需要借助几个 H5 播放器的事件和属性就可以了:
暂停和开始
暂停和开始是最基本的功能了,实现起来很简单
开始:
videoElement.play()
暂停:
videoElement.pause()
所以只需要在当前视频暂停的时候调用play()
方法就可以继续播放,同理在视频正在播放的时候调用pause()
就可以了。
提的一提的是『如何判断当前视频是否暂停』
如果仅用videoElement.paused
来判断视频暂停可能会有如下报错:
The play() request was interrupted by a call to pause().
经过上网查询,最后使用了isPlaying 变量来判断视频是否暂停
let isPlaying = videoElement.currentTime > 0 && !videoElement.paused && !videoElement.ended && videoElement.readyState > 2;
最后暂停/开始事件的方法如下:
let playpauseToggle = function(e){
let isPlaying = videoElement.currentTime > 0 && !videoElement.paused && !videoElement.ended && videoElement.readyState > 2;
if(!isPlaying) {
videoElement.play();
}
else {
videoElement.pause();
}
return false;
}
视频当前时长/总时长
这个就很容易了,借助两个参数即可;
duration
属性可以获取视频总时长
currentTime
属性可以获取视频当前时长
videoElement.duration //视频总时长
videoElement.currentTime//视频当前时长
视频总时长在视频载入后获取一次即可,
视频当前时间则不同,每当视频进度更新一次后,视频当前时间就需要随之更新
loadedmetadata
事件:获取视频元数据。
timeupdate
事件:视频播放后,更新播放进度的事件。 会有明确的进度变化,可以获取到currentTime
值得一提的是,用duration
和 currentTime
获取到的数据格式是保留了若干位小数,以秒为单位的一个值,通常需要根据需要格式化一下,我为了将『十分钟二十四秒』格式化为10:24
这样的格式,创建了如下方法
let numberToTime = function(number){
number = parseInt(number,10);
let minues = 0;
let second = 0;
minues = parseInt(number / 60, 10);
second = number % 60
if(minues<10){
minues = '0'+ minues
}
if(second<10){
second = '0'+ second
}
return minues + ':' + second;
}
以及监听对应事件并更新当前时间的代码如下
$videoElement.on('loadedmetadata', function() {
$('.duration').text(numberToTime(videoElement.duration));
});
$videoElement.on('timeupdate', function() {
$('.current').text(numberToTime(videoElement.currentTime));
});
<div class="progressTime">
<span class="current">00:00</span> / <span class="duration">00:00</span>
</div>
进度条
没有进度条的视频不是好视频,实现一个进度条很重要。
声明一点:进度条这里的样式我使用了 bootstrap 的进度条样式。
dom 结构如下:
<div class="progress">
<div class="progress-bar progress-bar-primary" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 0%;"> </div>
<div class="bufferBar progress-bar progress-bar-buffered"></div>
</div>
有两个条,一个是当前进度,一个是缓冲进度。
基本原理也很简单,获取 当前时间/总时长 * 100% 的值,然后把这个值赋给进度条对应 dom 的样式中的 'width',通过timeupdate
事件不断更新即可。
$videoElement.on('timeupdate', function() {
let currentPos = videoElement.currentTime;
let maxduration = videoElement.duration;
let percentage = 100 * currentPos / maxduration;
$('.progress-bar-primary').css('width', percentage + '%');
})
缓冲进度条和上面的实现基本一致,对应上面currentPos
值的是videoElement.buffered.end(0)
,缓冲进度条应该监听progress
事件,而不是 timeupdate
事件
progress事件: 当浏览器正在下载音频/视频时触发。
$videoElement.on('progress', function() {
let currentPos = videoElement.buffered.end(0);
let maxduration = videoElement.duration;
let percentage = 100 * currentPos / maxduration;
$('.progress-bar-primary').css('width', percentage + '%');
})
注意:此处只是讲述获取方法,具体样式和取值的变化要根据实际项目进行调整
进度条不但要能看进度,还要能拖拽进度
拖拽进度的思路是:根据鼠标在进度条上的mousedown
事件和mouseup
事件鼠标的 X 坐标量(含正负)变化来确定当前进度的变化程度。
具体代码如下:
let timeDrag = false; /* Drag status */
$('.progress').mousedown(function(e) {
timeDrag = true;
updatebar(e.pageX);
});
$(document).mouseup(function(e) {
if(timeDrag) {
timeDrag = false;
updatebar(e.pageX);
}
});
$(document).mousemove(function(e) {
if(timeDrag) {
updatebar(e.pageX);
}
});
let updatebar = function(x) {
let progress = $('.progress');
let maxduration = videoElement.duration; //视频总时长
let position = x - progress.offset().left; //变化量
let percentage = 100 * position / progress.width();
//超出范围的修正
if(percentage > 100) {
percentage = 100;
}
if(percentage < 0) {
percentage = 0;
}
//更新进度条和当前时间
$('progress-bar').css('width', percentage+'%');
videoElement.currentTime = maxduration * percentage / 100;
};
音量
音量的控制很容易,主要借助以下方法和属性,具体的交互可以随心所欲的实现。之前还有看过一篇文章是介绍丧心病狂的音量键的,可以学习学习[滑稽]:http://adquan.com/post-10-41045.html
videoElement.muted = true //静音
videoElement.muted = false //解除静音
videoElement.volume = 1 //设置音量为最大值
videoElement.volume = 0.5 //设置音量为50%
videoElement.volume = 0 //设置音量为最小值
倍速播放
倍速播放很容易,把交互稍微写好点就行,实现方式非常简单 用 playbackRate
就行,和控制音量差不多。
videoElement.playbackRate = 0.5; 0.5倍速播放
videoElement.playbackRate = 1;正常播放
videoElement.playbackRate = 1.5; 1.5倍速播放
videoElement.playbackRate = 2; 2倍速播放
全屏
全屏是一个兼容起来有点麻烦的功能。
如果你要为 firefox 写用户控件的话,调用全屏 api 的就得是 <video>
和<div class="controls"><div>
的共同父元素,不然会发现全屏后,你的用户控件看不到了。(但然,firefox 原生的用户控件比 chrome 的好看一百倍,所以你要是用原生的用户控件,那直接 element.requestFullscreen()
就行)
<div class="live__player">
<video></video>
<div class="controls"></div>
</div>
//全屏
function fullScreenOn(element) {
if(element.requestFullscreen) {
element.requestFullscreen();
} else if(element.mozRequestFullScreen) {
$('.live__player')[0].mozRequestFullScreen();
} else if(element.msRequestFullscreen){
element.msRequestFullscreen();
} else if(element.oRequestFullscreen){
element.oRequestFullscreen();
} else if(element.webkitRequestFullscreen)
{
element.webkitRequestFullScreen();
} else{
var docHtml = document.documentElement;
var docBody = document.body;
var videobox = document.getElementById('sfLive');
var cssText = 'width:100%;height:100%;overflow:hidden;';
docHtml.style.cssText = cssText;
docBody.style.cssText = cssText;
videobox.style.cssText = cssText+';'+'margin:0px;padding:0px;';
document.IsFullScreen = true;
}
$controls.css('z-index','2147483647');
}
此处传入的值就是videoElement
(videoElement = document.getElementById('video');)
另外一点,最后一行代码做的是:把我们自定义控件的 z-index 属性设置为浏览器最大值。原因是,webkit 内核的浏览器在全屏的时候,视频的 z-index 变为了浏览器允许的最大值,我们把自定义控件的 z-index 也设置为最大值,就能避免用户控件被视频遮住。
:-webkit-full-screen {
z-index: 2147483647;//最大值
}
同样的,退出全屏模式的代码如下:
function fullScreenOff() {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if(document.oRequestFullscreen){
document.oCancelFullScreen();
} else if (document.webkitExitFullscreen){
document.webkitExitFullscreen();
} else {
var docHtml = document.documentElement;
var docBody = document.body;
var videobox = document.getElementById('sfLive');
docHtml.style.cssText = "";
docBody.style.cssText = "";
videobox.style.cssText = "";
document.IsFullScreen = false;
}
$('.live__playcontrol').css('z-index','1');
}
检查视频是否全屏的代码如下:
let isFullScreen = document.fullscreenElement ||
document.webkitFullscreenElement ||
document.mozFullScreenElement ||
document.msFullscreenElement
踩坑
- 视频开始/暂停的判断依据
文中已写,代码如下
js
let isPlaying = videoElement.currentTime > 0 && !videoElement.paused && !videoElement.ended && videoElement.readyState > 2;
- firefox 全屏后,自定义的用户控件不见了
文中已写,和其他浏览器不同,firefox 不是用
<video>
元素的 dom 对象去调用requestFullscreen()
,而是用<video>
元素的父元素去调。 - shadowDom 隐藏的问题
如果你发现,你明明把 <video>
元素的属性controls
去掉了,但是原生控件依然显示,那么只有用 CSS 去干掉它
video::-webkit-media-controls-enclosure {
display:none !important;
}
可以在下列链接中查看 <video>
的属性和事件