播放器

目的:可快退、快进、静音、调节声音、全屏、改变当前视频所在位置(以拖动形式和点击进度条的形式)

遇到的问题:

① 谷歌浏览器上若用本地视频无法对video标签进行currentTime的设置
② 在video的oncanplay事件触发前无法获取该视频的duration
③ 只要视频的时间改变都会触发ontimeupdate事件,若想要1s进度条移动 & 1s时间变化一次,可采用对video.currentTime向下求整的方式来实现
④ 当videoplaybackRate改变时,其currentTime的改变速度变快,此时不是1s移动一次进度条 & 1s时间变化一次。而是与速度有关了,但时间仍是以整数变化的(无论是加速还是未加速,计算的currentTime都是整数值,时间和计算的currentTime有关,故仍以整数形式变化)。(不加快速度时,其默认播放速度为1,故可做到③)。
⑤ 在快退时,不可将video.playbackRate设为-1。应该在定时器中减少video.currentTime的值来实现。

1. 播放和暂停切换

在按播放时,视频开始播放,调用video.play()方法。在按暂停时,视频停止播放,调用video.pause()方法。在后续的快进和快退功能下,video.playbackRate会发生变化,当点击快进按钮时,若想要速度变为正常则需要点击暂停按钮,再点击播放按钮,此时播放速度变为正常速度

play.onclick = function () {
        clearInterval(timer);
        if(this.innerHTML=="播放"){
            this.innerHTML="暂停";
            video.play();
        }else{
            this.innerHTML = "播放";
            video.pause();
        }
        video.playbackRate = 1;
        range.value = initVolume * 100;
    };
2. 视频时间变化

在视频时间变化时,进度条、圆圈的位置以及当前视频时间也需要变化。由于视频时间只要变化就会调用ontimeupdate方法,若想1s变化一次,则可通过对currentTime向下求整的方式来实现。当视频播放到结尾时,currentTime变为0,暂停变为播放,即跳到视频的开始。用户点击播放可重新开始播放

video.ontimeupdate = function(){
        if(video.currentTime<=0){
            clearInterval(timer)
        }
        var currentTime = Math.floor(this.currentTime);//将当前时间向下求整做到变化几次当前时间后才移动进度条长度
        var width = (currentTime/this.duration)*parseInt(progressWidth);
        progressInner.style.width = width+"px";
        circle.style.left = (width-parseInt(circleWidth)/2)+"px";
        nowTime.innerHTML = setTime(currentTime);
        if(this.currentTime>=this.duration){
            play.innerHTML="播放";
            this.currentTime = 0;
        }
    };
3. 快进 & 快退

当点击快退时,创建计时器,视频停止播放。当快退至视频的开始时,清除计时器。

/*
    * 快进
    * */
    go.onclick = function () {
        video.playbackRate = 3;
    }
    /*
    * 快退
    * */
    back.onclick = function () {
        video.pause();
        play.innerHTML="播放";
        // video.playbackRate = -1;//不可为负数!!!
        timer = setInterval(function(){
            video.currentTime -= 2;
            console.log(video.currentTime)
        },500)
    }
    /*
    * 全屏
    * */
    allScreen.onclick = function () {
        /*
        * 考虑兼容性
        * */
        if (video.requestFullscreen) {
            video.requestFullscreen()
        }
        if (video.webkitRequestFullscreen) {
            video.webkitRequestFullscreen()
        }
        if (video.mozRequestFullscreen) {
            video.mozRequestFullscreen()
        }
        if (video.msRequestFullscreen) {
            video.msRequestFullscreen()
        }
        if (video.oRequestFullscreen) {
            video.oRequestFullscreen()
        }
    }
4. 点击进度条,跳到点击的位置

点击进度条,改变video.currentTime、进度条位置、圆圈位置和当前所在时间。

progress.onclick = function (e) {
        var offset = e.clientX-progress.offsetLeft-videoOut.offsetLeft;
        var currentTime = (offset/parseInt(progressWidth))*video.duration;
        video.currentTime  = currentTime;
        var width = (currentTime/video.duration)*parseInt(progressWidth);
        progressInner.style.width = width+"px";
        circle.style.left = (width-parseInt(circleWidth)/2)+"px";
        nowTime.innerHTML = setTime(currentTime);

    }
5. 当拖动圆圈时,改变视频播放位置

当点击圆圈进行拖动时才能进行视频位置的移动。此时触发圆圈的onousedown事件。在移动的过程中,不仅可以在进度条上移动,也可以在整个视频的范围内移动,故需要设置视频的onmousemove事件,在其onmouseup事件触发的同时清除其onmousemove事件

circle.onmousedown = function() {
        videoOut.onmousemove = function (e) {
            var offset = e.clientX - this.offsetLeft - progress.offsetLeft;
            if (offset <= 300 && offset >= 0) {
                video.currentTime = (offset / parseInt(progressWidth)) * video.duration;
                progressInner.style.width = offset + "px";
                circle.style.left = (offset - parseInt(circleWidth) / 2) + "px";
            }
        }
    };
     videoOut.onmouseup = function(){
        //若外层写成video.onmouseup事件,则在底部progress处无法触发。。因为progress将其挡住
        this.onmousemove = null;//清空事件内容
    };
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<style>
    .video{
        width: 600px;
        height: 400px;
        position: absolute;
        left: 0;
        right: 0;
        margin:auto;
        border:1px solid black;
        background: black;
    }
    .video>video{
        width: 600px;
        height: 400px;
    }
    .video>.controls{
        width: 100%;
        position: absolute;
        bottom: 0;
        height: 40px;
        background: white;
    }
    .controls>div:not(.time){
        border-radius:50%;
        color: white;
        width: 30px;
        height: 30px;
        text-align: center;
        line-height: 30px;
        background: black;
        font-size: 12px;
        position: absolute;
        top:0;
        bottom:0;
        margin:auto;
    }

    .go{
        left:32px;
    }
    .back{
        left:64px
    }
    .progress{
        border-radius:5px !important;
        left:96px;
        width: 300px !important;
        height: 6px !important;
        background: silver !important;
    }
    .progressInner{
        background:gray;
        height:100%;
        width: 0;
    }
    .progress>.circle{
        position:absolute;
        top:-3px;
        left:-6px;
        display: inline-block;
        border-radius:50%;
        background: black;
        width: 12px;
        height: 12px;
    }
    .mute{
        right: 64px;
    }
    .volume{
        right: 32px;
    }
    .allScreen{
        right: 2px;
    }
    .time{
        position: absolute;
       right: 98px;
        line-height: 40px;
        font-size: 12px;
    }
    #range{
        display: none;
        transform:rotate(-90deg) !important;
        position: absolute;
        right: -18px;
        bottom: 90px;
    }

</style>
<body>
<div class="video">
    <video src="http://smvideo.duowan.com/video_upload/14031.mp4"></video><!--谷歌浏览器上若用本地视频无法对其进行currentTime的设置-->
    <div class="controls">
        <div class="play">播放</div>
        <div class="go">快进</div>
        <div class="back">快退</div>
        <div class="progress">
            <div class="progressInner">
            </div>
            <span class="circle"></span>
        </div>
        <div class="time">
            <span class="nowTime">00:00:00</span>/
            <span class="allTime">00:00:45</span>
        </div>
        <div class="mute" data-num="0">静音</div>
        <div class="volume">音量</div>
        <div class="allScreen">全屏</div>
        <input type="range" value="0" min="0" max="100" id="range" orient="vertical">
    </div>
</div>
<script>
    var play = document.getElementsByClassName("play")[0];
    var range = document.getElementById("range");
    var go = document.getElementsByClassName("go")[0];
    var back = document.getElementsByClassName("back")[0];
    var video = document.getElementsByTagName("video")[0];
    var nowTime = document.getElementsByClassName("nowTime")[0];
    var videoOut = document.getElementsByClassName("video")[0];
    var allTime = document.getElementsByClassName("allTime")[0];
    var allScreen = document.getElementsByClassName("allScreen")[0];
    var progress = document.getElementsByClassName("progress")[0];
    var progressInner = document.getElementsByClassName("progressInner")[0];
    var circle = document.getElementsByClassName("circle")[0];
    var mute = document.getElementsByClassName("mute")[0];
    var volume = document.getElementsByClassName("volume")[0];
    var timer;
    var speed=1000;
    var oldSpeed;
    var initVolume;
    var nowVolume;
    var progressWidth = window.getComputedStyle(progress).width;
    var circleWidth = window.getComputedStyle(circle).width;
    circle.onmousedown = function() {
        videoOut.onmousemove = function (e) {
            var offset = e.clientX - this.offsetLeft - progress.offsetLeft;
            if (offset <= 300 && offset >= 0) {
                video.currentTime = (offset / parseInt(progressWidth)) * video.duration;
                progressInner.style.width = offset + "px";
                circle.style.left = (offset - parseInt(circleWidth) / 2) + "px";
            }
        }
    };
        // progress.onmousemove = function (e) {//只在进度条控制
        //     var offset = e.clientX-this.offsetLeft-videoOut.offsetLeft;
        //     if(offset<=300 && offset>=0){
        //         video.currentTime = (offset/parseInt(progressWidth))*video.duration;
        //         progressInner.style.width = offset+"px";
        //         circle.style.left = (offset-parseInt(circleWidth)/2)+"px";
        //     }
        // }
    videoOut.onmouseup = function(){
        //若外层写成video.onmouseup事件,则在底部progress处无法触发。。因为progress将其挡住
        this.onmousemove = null;//清空事件内容
    };
    volume.onclick = function(){
      if(range.style.display == "block"){
          range.style.display = "none";
      }else{
          range.style.display = "block";
      }
    };
    range.onchange = function(){
        var rate = this.value/this.max;
        video.volume = initVolume*rate;
        nowVolume = initVolume*rate;
    };
    mute.onclick = function(){
       if(this.getAttribute("data-num")=="0"){
           video.muted = true;
           this.setAttribute("data-num","1");
           range.value = 0;
           mute.style.background="red"
       }else{
           video.muted = false;
           this.setAttribute("data-num","0");
           range.value = nowVolume*range.max;
           mute.style.background=""
       }

    };
    video.oncanplay = function () {
        allTime.innerHTML = setTime(this.duration);
        oldSpeed = this.playbackRate;
        initVolume = this.volume;
        nowVolume = initVolume;
    };
    /*
    * 设置总时间和当前时间
    * */
    function setTime(time){
        var str = "";
        var time = time;
        var hour = Math.floor((time/(60*60))%(24));
        str += hour<10 ? "0"+hour : hour;
        var minute = Math.floor((time/60)%60);
        str += minute<10 ? ":0"+minute : ":"+minute;
        var second = Math.floor(time%60);
        str += second<10 ? ":0"+second : ":"+second;
       return str;
    }
    /*
    * 切换播放和暂停
    * */
    play.onclick = function () {
        clearInterval(timer);
        if(this.innerHTML=="播放"){
            this.innerHTML="暂停";
            video.play();
        }else{
            this.innerHTML = "播放";
            video.pause();
        }
        video.playbackRate = 1;
        range.value = initVolume * 100;
    };
    /*
    * 时间变化时调用
    * */
    video.ontimeupdate = function(){
        if(video.currentTime<=0){
            clearInterval(timer)
        }
        var currentTime = Math.floor(this.currentTime);//将当前时间向下求整做到变化几次当前时间后才移动进度条长度
        var width = (currentTime/this.duration)*parseInt(progressWidth);
        progressInner.style.width = width+"px";
        circle.style.left = (width-parseInt(circleWidth)/2)+"px";
        nowTime.innerHTML = setTime(currentTime);
        if(this.currentTime>=this.duration){
            play.innerHTML="播放";
            this.currentTime = 0;
        }
    };
    /*
    * 快进
    * */
    go.onclick = function () {
        video.playbackRate = 3;
    }
    /*
    * 快退
    * */
    back.onclick = function () {
        video.pause();
        play.innerHTML="播放";
        // video.playbackRate = -1;//不可为负数!!!
        timer = setInterval(function(){
            video.currentTime -= 2;
            console.log(video.currentTime)
        },500)
    }
    /*
    * 全屏
    * */
    allScreen.onclick = function () {
        /*
        * 考虑兼容性
        * */
        if (video.requestFullscreen) {
            video.requestFullscreen()
        }
        if (video.webkitRequestFullscreen) {
            video.webkitRequestFullscreen()
        }
        if (video.mozRequestFullscreen) {
            video.mozRequestFullscreen()
        }
        if (video.msRequestFullscreen) {
            video.msRequestFullscreen()
        }
        if (video.oRequestFullscreen) {
            video.oRequestFullscreen()
        }
    }

    /*
    * 点击进度条
    * */
    progress.onclick = function (e) {
        var offset = e.clientX-progress.offsetLeft-videoOut.offsetLeft;
        var currentTime = (offset/parseInt(progressWidth))*video.duration;
        video.currentTime  = currentTime;
        var width = (currentTime/video.duration)*parseInt(progressWidth);
        progressInner.style.width = width+"px";
        circle.style.left = (width-parseInt(circleWidth)/2)+"px";
        nowTime.innerHTML = setTime(currentTime);

    }
</script>
</body>
</html>