写了个简单的H5视频播放器

写在前面

好久没跟新博客了。最近一直忙着恶补JavaScript的知识,没办法,小白就要多练多看。这两天写了个简单的视频播放器,实现了一些简单的功能。今天就把这个简单的播放器分享出来,仅供参考。希望能对大家有点帮助。。。尽管这个写的比较low。。

效果功能

效果预览

效果如下图:

实现功能

播放器是用原生js写的,实现了一些基本的功能:

  • 播放/暂停、停止、空格键暂停/播放;
  • 音量加减、支持拖动调节音量以及上下方向键调整音量;
  • 支持静音、全屏播放;
  • 支持发送弹幕,弹幕颜色随机、出现位置随机;
  • 支持播放列表点击切换、支持自动循环列表播放。

目前还存在的bug

目前发现,在播放过程中,通过键盘上下方向键调节音量后,鼠标划入视频区域,控制条无法显示。。暂未解决。

源码分享

说明:源代码中引入了font-awesome的字体图标库。

HTML部分代码

页面结构布局较简单,下面是HTML部分的代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>视频播放器</title>
<link rel="stylesheet" type="text/css" href="css/main.css">
<link href="css/font-awesome.min.css" rel="stylesheet" type="text/css">
</head>
<body>
<div class="box clearFix">
<div class="vbox fl"><!-- 视频盒子部分开始 -->
<video id="myvideo" preload="auto"></video>
<div class="ctrlbox clearFix"><!-- 播放控制条部分开始 -->
<div class="left fl">
<i class="fa fa-play" id="playbtn" title="点击播放"></i>&nbsp;
<i class="fa fa-stop" id="stopbtn" title="停止"></i>
<span id="nowtime">00:00</span><span id="sep">/</span><span id="fulltime"></span>
<span class="progress" title="点击跳到这儿"><!-- 进度条部分开始 -->
<span id="pgbar"></span>
</span><!-- 进度条部分结束 -->
</div>
<div class="right fr">
<i class="fa fa-volume-up" id="volume" title="点击静音"></i>
<i class="fa fa-expand" id="expand" title="全屏"></i>
</div>
<div class="volctrl"><!-- 音量控制部分开始 -->
<span id="volnum">100</span>
<div id="volbarcon">
<span id="volbar"></span>
</div>
</div><!-- 音量控制部分结束 -->
</div><!-- 播放控制条部分结束 -->
<div class="mask"><i id="mbtn" class="fa fa-play-circle" title="点击播放"></i></div>
</div><!-- 视频盒子部分结束 -->

<div class="playlist fl"><!-- 播放列表部分开始 -->
<h3>播放列表</h3>
<ul id="list">
</ul>
</div><!-- 播放列表部分结束 -->

<div class="msgbox"><!-- 弹幕部分开始 -->
<label>昵称:</label>
<input type="text" id="usrname">
<label>弹幕:</label>
<input type="text" id="message">
<input type="button" id="send" value="发送" />
</div>
</div><!-- 弹幕部分结束 -->

<script type="text/javascript" src="js/main.js"></script>
</body>
</html>

CSS代码

@charset "UTF-8";
body,ul,li{
margin: 0;
padding: 0;
}

.fl{
float: left;
}

.fr{
float: right;
}

.clearFix:after{
content: '';
display: block;
clear: both;
}

/*大盒子样式*/
.box{
width: 1000px;
height: 450px;
margin: 0 auto;
}

/*视频盒子部分*/
.vbox{
position: relative;
width: 800px;
height: 450px;
overflow: hidden;
}

.vbox:hover .ctrlbox{
display: block;
}

/*屏幕内弹幕span样式*/
.vbox span{
position: absolute;
white-space: nowrap;
font-size: 18px;
}

/*控制条部分按钮样式*/
.vbox .ctrlbox span{
color: #fff;
position: relative;
}

/*视频盒子中间播放按钮*/
.mask{
width: 90px;
height: 90px;
position: absolute;
top: 180px;
left: 355px;
text-align: center;
}

#mbtn{
position: relative;
z-index: 900;
display: block;
height: 90px;
width: 80px;
padding: 0 5px;
margin: 0;
font-size: 90px;
color: #000;
line-height: 90px;
}

/*时间进度部分*/
/*视频总时间颜色*/
#fulltime{
color: #afafaf;
}

/*时间显示样式*/
#nowtime,
#fulltime
{
display: inline-block;
width: 44px;
font-size: 16px;
line-height: 24px;
top: -3px;
}

/*视频大小*/
#myvideo{
width: 800px;
height: 450px;
transition: all 1s ease;
}

/*控制盒子部分*/
.ctrlbox{
width: 770px;
height: 50px;
position: absolute;
left: 0;
bottom: 0;
padding: 0 10px 0 20px;
background-color: #141414;
font-size: 24px;
line-height: 50px;
color: #fff;
display: none;
}

/*按钮字体样式*/
i{
font-style: normal;
display: inline-block;
width: 30px;
height: 30px;
margin-right: 5px;
text-align: left;
cursor: pointer;
}

/*播放按钮*/
#playbtn{
display: inline-block;
width: 20px;
height: 24px;
text-align: center;
}

#sep{
position: relative;
top: -2px;
}

/*进度条部分*/
.progress{
z-index: 50;
position: relative;
display: inline-block;
width: 480px;
height: 8px;
margin-bottom: 4px;
margin-left: 6px;
border-radius: 8px;
overflow: hidden;
background-color: #fff;
cursor: pointer;
}

#pgbar{
z-index: 10;
position: absolute;
left: 0;
top: 0;
display: inline-block;
height: 8px;
border-radius: 8px;
background-color: #9f9f9f;
}

#volume{
line-height: 50px;
}

/*音量控制部分*/
/*音量控制盒子*/
.volctrl{
width: 40px;
height: 110px;
padding: 5px 0;
background-color: #141414;
text-align: center;
position: absolute;
right: 58px;
bottom: 45px;
display: none;
}

/*音量大小数值*/
#volnum{
position: absolute;
display: block;
width: 100%;
height: 20px;
font-size: 14px;
line-height: 20px;
}

/*音量条盒子*/
#volbarcon{
display: block;
margin-left: 16px;
margin-top: 20px;
width: 8px;
height: 90px;
border-radius: 8px;
background-color: #fff;
position: absolute;
z-index: 100;
overflow: hidden;
cursor: pointer;
}

/*音量条*/
#volbar{
position: absolute;
left: 0;
bottom: 0;
z-index: 20;
display: block;
width: 8px;
height: 100%;
border-radius: 8px;
background-color: #9f9f9f;
}

/*播放列表部分*/
.playlist{
height: 450px;
width: 200px;
position: relative;
background-color: #1f1f1f;
}

h3{
margin: 10px 0 0;
padding: 0;
font-size: 20px;
color: #fff;
padding-left: 10px;
}

#list{
width: 100%;
margin-top: 20px;
}

/*播放列表项样式*/
#list li{
list-style: none;
width: 185px;
height: 40px;
margin: 0 5px 5px;
padding-left: 5px;
font-size: 14px;
line-height: 40px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
color: #fff;
background-color: #2F2F2F;
cursor: pointer;
}

#list li:hover{
background-color: #4f4f4f;
}

/*发送弹幕区域*/
.msgbox{
clear: both;
width: 980px;
height: 50px;
padding: 0 10px;
margin: 10px auto 0;
}

/*弹幕部分文本框样式*/
input{
width: 200px;
font-size: 16px;
padding: 8px;
}

#usrname{
width: 120px;
margin-right: 20px;
}

label{
font-size: 16px;
line-height: 30px;
}

/*发送按钮*/
#send{
width: 80px;
padding: 5px;
border-radius: 3px;
margin-top: 10px;
margin-left: 20px;
font-size: 20px;
color: #fff;
line-height: 26px;
border: 0;
outline: none;
cursor: pointer;
background-color: #1C96D6;
}

js代码

以下是js代码部分,注释较为详细,应该很容易看明白。

// 视频文件名存在数组之中
var names = ["Taylor Swift - Begin Again.mp4","Taylor Swift - Safe & Sound.mp4","Taylor Swift - Red.mp4","Taylor Swift - I Don’t Wanna Live Forever.mp4","我怀念的 - 林俊杰.mp4"];
var list = document.getElementById("list");
var myvideo = document.getElementById("myvideo");
var mask = document.getElementsByClassName("mask")[0];
var mbtn = document.getElementById("mbtn");
var ctrlbox = document.getElementsByClassName("ctrlbox")[0];
var play = document.getElementById("playbtn");
var stop = document.getElementById("stopbtn");
var mute = document.getElementById("volume");
var expand = document.getElementById("expand");
var nowtime = document.getElementById("nowtime");
var fulltime = document.getElementById("fulltime");
var pgbar = document.getElementById("pgbar");
var index; // 定义播放指针

// 动态添加视频播放列表
for(var i = 0; i < names.length; i++){
var li = document.createElement("li");
// 设置列表内容为对应的视频名
li.innerText = names[i];
// 鼠标停留在某个li上时显示当前视频文件名,使用title属性
li.title = li.innerText;
list.appendChild(li); // 将视频添加至列表
}

// 设置初始状态视频地址
var lis = list .getElementsByTagName("li");
myvideo.src = "video/" + lis[0].innerText;
lis[0].style.backgroundColor = "#5f5f5f"; // 设置当前选中视频在列表中高亮
index = 0;

// 当视频可以播放时,显示视频总时间
myvideo.oncanplay=function(){
// 视频总长度
fulltime.innerText = secTomin(myvideo.duration);
}

// 定义时间转换函数,“秒”转 “分+秒”
function secTomin(seconds){
var s = Math.round(seconds % 60);
if(s < 10){
s = "0" + s;
}
var m = Math.floor(seconds/60);
if(m < 10){
m = "0" + m;
}
var t = m + ":" + s;
return t;
}

// 进度条部分 + 显示视频当前播放时间
//视频播放中根据播放进度更新显示进度条,监听视频时间更新事件
myvideo.addEventListener('timeupdate', function() {
var currentPos = this.currentTime; //获取当前播放的位置
//更新进度条
var percentage = 100 * currentPos / myvideo.duration;
pgbar.style.width = percentage + '%';
// 显示视频当前播放时间
nowtime.innerText = secTomin(currentPos);
// 如果视频播放结束,调用循环播放函数实现列表循环播放
if(myvideo.ended){
setTimeout(loopPlay, 1000); // 1s后开始播放下一首,预留一点加载时间
}
});

// 列表循环播放函数
function loopPlay(){
// 判断并设置播放指针
// 如果当前是列表最后一个视频,跳到第一个视频,否则继续下一个
if(index == lis.length-1){
index = 0
}
else{
index += 1;
}
// 播放列表状态跟随播放视频自动切换
// 先清空上一个列表项的高亮激活状态,恢复为未选中时候的背景色
for(var j = 0; j < lis.length; j++){
lis[j].style.backgroundColor = "#2f2f2f";
}
// 设置当前选中列表项的背景色为高亮激活状态
lis[index].style.backgroundColor = "#5f5f5f";
// 切换视频地址为下一首地址,并自动播放
myvideo.src = "video/"+lis[index].innerText;
play.onclick();
}

// 播放进度条部分
// 鼠标在播放条上点击时,实现实时跳转播放
var progress = document.getElementsByClassName("progress")[0];
// 定义函数获取点击位置X坐标,得到点击位置距离浏览器左边界的距离
progress.onmousedown = function(e){
e = e || window.event;
updatebar(e.pageX);
}

function updatebar(x){
//计算点击位置距离进度条左边的距离,注意!!!!
// 得到的X的值是当前点击位置距离浏览器左边界的距离
// 而进度条是相对于vbox绝对定位的
// 得到的progress.offsetLeft是相对于播放器左侧边界定位的
// 因此要得到进度条点击后的长度还要减掉vbox.offsetLeft
var positions = x - progress.offsetLeft - vbox.offsetLeft;
var percent = 100 * positions / (progress.offsetWidth);
// 细小误差容错、纠正
if (percent > 100) {
percent = 100;
}
if (percent < 0) {
percent = 0;
}
// 更新进度条宽度 以及 视频当前播放时间currenttime
pgbar.style.width = percent + '%';
myvideo.currentTime = myvideo.duration * percent / 100;
};

// 音量部分
// 音量控制进度条
var volnum = document.getElementById("volnum");
var volbarcon = document.getElementById("volbarcon");
var volbar = document.getElementById("volbar");

volbarcon.onclick = function(e){
e = e || window.event;
volControl(e.pageY);
}

// 音量按钮鼠标滑过显示音量控制条,鼠标移出隐藏音量控制条
var volctrl = document.getElementsByClassName("volctrl")[0];
mute.onmouseover = function(){
volctrl.style.display = "block";
}

mute.onmouseout = function(){
volctrl.style.display = "none";
}

// 鼠标在音量条上移动时候继续显示音量控制条
volctrl.onmousemove = function () {
volctrl.style.display = "block";
}
// 鼠标从音量条上移出时候继续显示音量控制条
volctrl.onmouseout = function () {
volctrl.style.display = "none";
}

// 监控音量变化,实时显示当前音量
myvideo.addEventListener('volumechange', function() {
// 显示视频当前音量
// 如果视频静音,则显示为0
if(myvideo.muted){
volnum.innerText = "0";
}
else{
volnum.innerText = parseInt(myvideo.volume*100);
}
});

// 音量控制函数
function volControl(y){
// 注意百分比计算方法:(声音条下方距离顶部距离-点击位置的Y坐标)/声音条高度
// 这里的声音条下端和视频盒子控制条最上边对齐,方便计算百分比
// 另外注意音量范围为 0~1
var curpos = vbox.offsetHeight-ctrlbox.offsetHeight - y;
var per = 100 * curpos / (volbarcon.offsetHeight);
volbar.style.height = per + '%';
myvideo.volume = per / 100;
// 如果音量=0,更换图标为volume-off,变暗,显示颜色为#ccc
if(myvideo.volume == 0){
volume.className = "fa fa-volume-off";
mute.style.color = "#ccc";
}
// 如果音量在0-0.5之间,更换图标为volume-down
else if(myvideo.volume>0 && myvideo.volume < 0.5){
volume.className = "fa fa-volume-down";
mute.style.color = "#fff";
}
// 如果音量在0.5-1之间,更换图标为volume-up
else{
volume.className = "fa fa-volume-up";
mute.style.color = "#fff";
}
}

// 播放控制
// 点击按钮实现播放,按钮改为暂停按钮(点击暂停)
// 再次点击按钮暂停,按钮切换为播放按钮(点击播放)
play.onclick = function(){
if(!myvideo.paused){
myvideo.pause();
play.className = "fa fa-play";
play.title = "点击播放";
// 视频暂停时,大按钮才显示
mask.style.display = "block";
}
else{
myvideo.play();
play.className = "fa fa-pause";
play.title = "点击暂停";
// 视频播放时,大按钮隐藏
mask.style.display = "none";
}
}

// 点击键盘按键控制播放
document.onkeydown=function(event){
var e = event || window.event || arguments.callee.caller.arguments[0];
// 点击空格键实现播放、暂停
if(e && e.keyCode==32){ // 按 空格键
play.onclick();
}
// 点按 ENTER键 发送弹幕,先判断是否填写完整
if(e && e.keyCode==13){ // 按 ENTER键
// 如果弹幕信息均不为空,则发送弹幕
if(usrname.value !=="" && message.value !==""){
send.onclick();
}
// 只要有一项未填写,则不发送弹幕,弹窗提醒
else{
if(usrname.value ==""){
alert("请填写昵称后再发送!");
}
else{
alert("请填写弹幕内容后再发送!");
}
}
}
// 上下方向键控制音量
// 按↑方向键,音量+1,音量条高度与音量同步
if(e && e.keyCode==38){ // ↑方向键
// 调出播放控制条、音量条
showctrl();
if(myvideo.volume !== 1){
myvideo.volume += 0.01;
volbar.style.height = myvideo.volume * 100 + "%";
}
else{
myvideo.volume = 1;
}
// 2s后隐藏控制条和音量条
setTimeout(hidectrl,2000);
}
// 按↓方向键,音量-1,音量条高度与音量同步
if(e && e.keyCode==40){ // ↓方向键
// 调出播放控制条、音量条
showctrl();
if(myvideo.volume !== 0){
myvideo.volume -= 0.01;
volbar.style.height = myvideo.volume * 100 + "%";
}
else{
myvideo.volume = 0;
}
// 2s后隐藏控制条和音量条
setTimeout(hidectrl,2000);
}
// 左右方向键实现前进、后退
// 按右方向键,进度+10s,进度条宽度与时间同步
// 判断距离结束时间大于15s时才可以快进
if(e && e.keyCode==39){ // →方向键
// 调出控制台
ctrlbox.style.display = "block";
if(myvideo.duration - myvideo.currentTime > 15){
myvideo.currentTime += 10;
pgbar.style.width = myvideo.currentTime/myvideo.duration + "%";
}
// 2s后隐藏播放控制条
setTimeout(function(){
ctrlbox.style.display = "none";
}, 2000);
}
// 按左方向键,进度-10s,进度条宽度与时间同步
// 判断距离开始时间大于15s时可以快退
if(e && e.keyCode==37){ // ←方向键
// 调出控制台
ctrlbox.style.display = "block";
if(myvideo.currentTime > 15){
myvideo.currentTime -= 10;
pgbar.style.width = myvideo.currentTime/myvideo.duration + "%";
}
// 2s后隐藏播放控制条
setTimeout(function(){
ctrlbox.style.display = "none";
}, 2000);
}
};

// 调出播放控制条和音量控制条函数
function showctrl(){
ctrlbox.style.display = "block";
volctrl.style.display = "block";
}
// 隐藏播放控制条和音量控制条函数
function hidectrl(){
ctrlbox.style.display = "none";
volctrl.style.display = "none";
}

// 点击列表项实现视频切换,并自动播放
for(var i = 0; i < lis.length; i++){
// lis[i].index = i;
lis[i].onclick = function(){
myvideo.src = "video/" + this.innerText;
play.onclick();
// 先清空上一个列表项的高亮激活状态,恢复为未选中时候的背景色
for(var j = 0; j < lis.length; j++){
lis[j].style.backgroundColor = "#2f2f2f";
}
// 设置当前选中列表项的背景色为高亮激活状态
this.style.backgroundColor = "#5f5f5f";
// 传递播放指针
index = this.index;
}
}

// 点击视频实现播放/暂停功能
myvideo.onclick = function(){
play.onclick();
}

// 双击视频全屏播放
myvideo.ondblclick = function(){
expand.onclick();
}

// 点击停止按钮停止播放
stop.onclick = function(){
myvideo.currentTime = "0";
myvideo.pause();
}

// 点击静音按钮实现静音,再次点击恢复音量,同时音量条高度与音量同步
mute.onclick = function(){
if(!myvideo.muted){
myvideo.muted = true;
this.className = "fa fa-volume-off";
this.style.color = "#ccc";
this.title = "取消静音";
// 点击静音后音量条归零
volbar.style.height = "0";
}
else{
myvideo.muted = false;
this.className = "fa fa-volume-up";
this.style.color = "#fff";
this.title = "点击静音";
// 点击取消静音后音量条恢复
volbar.style.height = myvideo.volume * 100 + "%";
volnum.innerText = myvideo.volume * 100;
}
}

// 点击全屏按钮实现全屏播放
expand.onclick=function(){
requestFullScreen(myvideo);
};

// 全屏播放函数
function requestFullScreen(element) {

var requestMethod = element.requestFullScreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || element.msRequestFullScreen;

if (requestMethod) {
requestMethod.call(element);
}
else if (typeof window.ActiveXObject !== "undefined") {
var wscript = new ActiveXObject("WScript.Shell");
if (wscript !== null) {
wscript.SendKeys("{F11}");
}
}
}

// 点击屏幕中间的大按钮实现播放、暂停
mbtn.onclick = function(){
play.onclick();
}

// 弹幕部分
var vbox = document.getElementsByClassName("vbox")[0];
var user = document.getElementById("usrname");
var message = document.getElementById("message");
var send = document.getElementById("send");
var str = "";
var timer;

// 生成弹幕 发送弹幕
send.onclick = function(){
str = user.value + " : " + message.value;
var span1 = document.createElement("span");
span1.innerText = str;
vbox.appendChild(span1);
span1.style.top = Math.floor(Math.random()*401) + "px"; // 随机生成弹幕距离视频上边距的距离
span1.style.left = "800px";
span1.style.color = "#" +Math.floor(Math.random()*0xffffff).toString(16); //随机生成颜色

// 设置定时器实现弹幕自动滚动
timer = setInterval(function(){
span1.style.left = (parseInt(span1.style.left)-1) + "px";
// 如果弹幕离开视频左边界一段距离,则删除该弹幕
if(span1.style.left == "-400px"){
vbox.removeChild(span1);
}
// 如果屏幕里无弹幕,则清空定时器timer
var spans = vbox.getElementsByTagName("span");
if(spans.length == 0){
clearInterval(timer);
}
}, 18);

// 弹幕生成完毕发送后,清空输入框,文本框失去焦点
str = "";
user.value = "";
message.value = "";
user.blur();
message.blur();
}