实现H5视频播放器的部分播放控件

GitHub链接: https://github.com/cyjsysu/H5_video_player

1.为什么写播放器

上学期(2019年末)数据库大作业原本是想做一个简易的视频网站的。但上网看了一些相关内容后,当时觉得自己还不会JS无法自定义样式。于是放弃原计划,改做音乐网站。现在有了一些JS基础,所以又把以前的想法拿了出来。这篇文章相当于一份简陋的实验报告。
(小项目中用到的图标全来自百度图片)

2.效果图

非全屏.PNG
非全屏显示音量.PNG
全屏显示控件.PNG
全屏隐藏控件.PNG

3.自定义控件

如果在HTML中设置video的controls属性,浏览器会显示自带的相应控件。

<video controls="controls" />

一开始我以为自定义控件是通过设置controls属性来实现的。但后来我看了B站播放页面的HTML,发现B站的控件都是DIV标签。然后我才知道这些控件在HTML里是独立于video标签的,只是通过JS控制视频而已。

4.视频播放和暂停

4.1要求

  • 点击播放窗口和播放键都能控制视频的播放或暂停
  • 暂停状态下播放按键图标为三角形,否则是两条竖杆

4.2实现

每次点击时根据当前视频的播放状态(Video.paused)切换到相反状态,并更改图标即可。
Video对象属性可参考https://www.runoob.com/jsref/dom-obj-video.html。这些属性在这个小项目里会用得比较多。

function playOrPause(){
    var oPlayer = document.getElementById("player");  //video
    let oPlayerBtn = document.getElementById("playerBtn");  //播放按钮

    if(oPlayer.paused==true){
        oPlayer.play();
        oPlayerBtn.src="./img/pause1.png";
    }else{
        oPlayer.pause();
        oPlayerBtn.src="./img/on1.png";
    }
}

5.进度条

5.1要求

  • 通过进度条实时显示当前视频的播放进度和缓冲进度
  • 播放进度为蓝色,在上;缓冲进度为浅灰色,在下(模仿B站)。
  • 能通过拖动进度条调整播放位置
  • 进度条旁边显示播放时间

5.2实现

5.2.1进度条的显示

我早期看了一篇类似的博客(基本只做出了播放器控件的样子)。文章里的进度条是一个<meter>标签,导致我被误导了一段时间。后来我又是看了B站的HTML才重新找到方向。
这里自定义的进度条实际上是一个div,呈现条状是因为属性height设得很小,设置背景色即进度条。
我分别写了三个这样的div。一个为深灰色,作为进度条的背景,并在其中分别全套其他两个div。另外两个其中一个是浅灰,作为缓冲条;另一个是蓝色,作为播放进度条。另外再设一个div作为播放进度条末端的拖动按钮。

<div id="controlBar">
  <div id="allBar">
    <div id="bufferBar"></div>
    <div id="playerBar"></div>
    <div id="barButton"></div>
  </div>
</div>

5.2.2拖动进度条

用JS实现,在进度条上按下鼠标触发。在鼠标按下的情况下移动鼠标会使播放进度条末端和鼠标水平坐标相同(不能超出整个进度条),并通过计算更新video的currentTime属性。
因为代码中有好几处用到document.onmousemove,所以不直接绑定函数,而是使用事件监听。

function dragProgress(){
    var oControlBar = document.getElementById("controlBar");

    oControlBar.onmousedown = function(evt){
        var e = evt || window.event;
        if(e.button == 0){
            leftX = getOffsetX(oControlBar);
            rightX = leftX + parseInt(getComputedStyle(oControlBar)["width"]);

            document.addEventListener("mousemove", toDragProgress);
        }
    };

    document.addEventListener("mouseup", function(){
        document.removeEventListener("mousemove", toDragProgress);
    })
}

function toDragProgress(evt){

    var e = evt || window.event;
    var buttonX = e.pageX;
    if(buttonX < leftX)
        buttonX = leftX;
    if(buttonX > rightX)
        buttonX = rightX;
    var prog = (buttonX - leftX) / (rightX - leftX);
    // console.log(prog);
    setProgress(prog);
}

5.2.3实时显示进度和时间

用定时器实现,每秒执行一次。

function updateProgressBar() {
    var oPlayer = document.getElementById("player");
    var oPlayerBar = document.getElementById("playerBar");
    var oBufferBar = document.getElementById("bufferBar");
    var oBarButton = document.getElementById("barButton");
    var oShowTime = document.getElementById("showTime");

    var cTime = oPlayer.currentTime;
    var dTime = oPlayer.duration;

    var playerProgress = cTime / dTime * 100;
    oPlayerBar.style.width = playerProgress + "%";
    oBarButton.style.left = playerProgress + "%";
    if (player.buffered.length){
        var bTime = oPlayer.buffered.end(0);
        var bufferProgress = bTime / dTime * 100;
        oBufferBar.style.width = bufferProgress + "%";
    }
    // duration是一个全局变量
    if(duration == 0)
        duration = getTime(oPlayer.duration);
    var currentTime = getTime(oPlayer.currentTime);
    oShowTime.innerText = currentTime + "/" + duration;
}

setInterval("updateProgressBar()", 1000);  //每秒

6.音量

6.1要求

音量控制的外观也是参考着B站的效果来做的(我的没音量百分比)。

  • 平时播放器只有下方那个音量图标,把鼠标移到上面时显示出音量控制条(display:block;)。当鼠标移出一段时间后,音量控制条消失(display:none;)。
  • 点击音量键变为静音,并且图标变成带"X"的喇叭。再点击静音后的音量键,音量回复静音前的值。


    B站音量

6.2实现

6.2.1显示和隐藏用来控制条

对图标设置mouseover事件。当鼠标在这个div以及子节点的div内时移除用来隐藏控制条的延时器,并显示音量条。这里要计算音量条的位置,使音量条在音量图标正上方,且不紧贴。
当鼠标移出,设置延时器,一段时间后隐藏音量条。

var hideVol = null;  // 隐藏音量条事件

 // 显示音量条
oControlVol.onmouseover = function(){
  clearTimeout(hideVol);  // 只要鼠标在音量框内就不会隐藏音量框

  // 设置的音量图标和音量条是分离的,计算音量条框位置
  var tarX = oControlVol.offsetLeft + (parseInt(getComputedStyle(oControlVol)["width"]) - parseInt(getComputedStyle(oVolFrame)["width"])) / 2;
  var tarY = oControlVol.offsetTop - parseInt(getComputedStyle(oVolFrame)["height"]) - 20;

  oVolFrame.style.left = tarX + "px";
  oVolFrame.style.top = tarY + "px";
  oVolFrame.style.display = "block";
};

 // 隐藏音量条
oControlVol.onmouseout = function(){
  hideVol = setTimeout(function () {
  var oVolFrame = document.getElementById("volumeFrame");
            oVolFrame.style.display = "none";
  }, 600);
};

6.2.2拖动音量条

实现过程和视频进度条差不多。

7.全屏

7.1要求

  • 点击全屏图标进入全屏,再次点击退出全屏
  • 进入全屏后不显示浏览器自带的播放控件,而是显示自己的控件
  • 进入全屏后当鼠标移动或者鼠标在播放控件上时,控件能一直显示;否则隐藏控件。
  • 进入全屏前控件是在视频下方的,除了音量条外两者不重叠。但进入全屏后要求控件和视频重叠并且以一定的透明度显示在视频最下方。

7.2实现

7.2.1进入和退出全屏

可参考https://www.jianshu.com/p/54729c73686a
讲得挺全的。

7.2.2全屏下控件显示

对<video>结点全屏,浏览器会显示显示自带的播放控件。这涉及到shadow DOM。在CSS文件中禁用。

video::-webkit-media-controls{
    display:none !important;
}

但这不是重点。因为即使禁用了自带控件也不会显示自己的控件。
正确的做法是整个div(包括video和控件)执行全屏,上面那段css也不需要。

7.2.3显示和隐藏控件

其实到这里很多问题实际上都是和上面重复的了。
隐藏和显示是通过控制栏的display属性实现的。鼠标移动时(document.onmousemove)显示控制栏,同时设置一个延时器来隐藏控制栏。每次触发document.onmousemove时要先移除现有的延时器。
在写这一部分时我碰到一个小坑。我设置鼠标进入控制栏时移除隐藏控制栏的延时器。但实际上仍会有延时器来隐藏控制栏。也就是说,即使鼠标在控制栏上,如果鼠标不动了,控制栏仍会被隐藏。后来发现这是因为,虽然鼠标在控制栏上时去除延时器,但当鼠标移动又会触发document.onmousemove重新设定定时器。解决方法时当鼠标进入控制栏时对document.onmousemove移除显示控制栏的函数showControl,移出时在用事件监听加回来。

    document.addEventListener("mousemove", showControl);
    oController.onmouseover = function(){
        document.removeEventListener("mousemove", showControl);
        ///console.log("H");
        clearTimeout(hide_control);
    };
    oController.onmouseout = function () {
        document.addEventListener("mousemove", showControl);
    }

8.不足

“下一集”的功能没实现,因为项目里没考虑复杂的应用场景。但要用的时候实现也不难。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355