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.不足

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


PANDAS
1 声望0 粉丝