原生JavaScript实现video视频控制栏

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./test.css">
    <!--  -->
</head>

<body>
    <div class="common-player_video_container_wrapper" id="videoplayer">
        <div class="common-player_video_container">
            <video class="" id="video" src="./1.mp4">
                
            </video>
        </div>
        <div class="common-player_video_control">
            <div class="_video_control_top">
                <div class="_video_control_left">
                    <div class="_video_control_start_wrapper">
                        <span class="_video_control_btn _video_control_play"></span>
                    </div>

                    <div class="_video_control_time_wrapper">
                        <span class="_video_control_font current_time">00:00:00</span>
                        <span class="_video_control_font">/</span>
                        <span class="_video_control_font total_time"></span>
                    </div>
                </div>
                <div class="_video_control_right">
                    <div class="_video_control_voice_compnent">
                        <div class="_video_control_voice_progress_wrapper bar">
                            <div class="_video_control_voice_progress progress"></div>
                            <div class="_video_control_voice_progress_btn dot"></div>
                        </div>
                        <div class="_video_control_voice_wrapper">
                            <span class="_video_control_btn _video_control_soundon"></span>
                        </div>
                    </div>
                    <div class="_video_control_fullscreen_wrapper">
                        <span class="_video_control_btn _video_control_fullscreen"></span>
                    </div>
                    <div class="_video_control_speed_wrapper">
                        <div class="_video_control_speed_panel   _video_control_font">
                            <ul>
                                <li>0.5</li>
                                <li class="_active">1.0</li>
                                <li>1.5</li>
                                <li>2.0</li>
                            </ul>
                        </div>
                        <span class="_video_control_speed _video_control_font">1.0</span>
                    </div>

                </div>
            </div>
            <div class="_video_control_bottom ">
                <div class="_video_control_progress_wrapper bar">
                    <div class="_video_control_progress progress"></div>
                    <div class="_video_control_progress_btn dot"></div>
                </div>

            </div>

        </div>
    </div>
<script src="./test.js"></script>
</body>

</html>
 /* 基础布局*/

 .common-player_video_container_wrapper {
    position: relative;
    width: 500px;
    /* height: 500px; */
    background-color: #000;
    margin: auto;

}

.common-player_video_container_wrapper video {
    width: 100%;
}

ul {
    list-style: none;
    margin: 0;
    padding: 0;

}

ul li {
    text-align: center;
    padding: 6px 0;

}

ul li:hover {
    background-color: #888888;
}


/* .common-player_video_flv:hover .common-player_video_control {
    display: block;
} */

.common-player_video_container {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
}

.common-player_video_control {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 60px;
    padding-top: 10px;
    /* background-color: rgba(255, 255, 255, 0.8); */
    background: -moz-linear-gradient(top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1));
    /* Firefox */
    background: -webkit-linear-gradient(top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1));
    /* Safari 和 Chrome */
    background: -o-linear-gradient(top, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1));
    /* Opera */
    background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1));
    /* 标准的语法 */
}

._video_control_top {
    width: 100%;
    height: 30px;
    /* background-color: #f66; */
    display: flex;
    justify-content: space-between;
    align-items: center;
}

._video_control_bottom {
    width: 100%;
    height: 18px;
    padding: 0 10px;
    box-sizing: border-box;
    display: flex;
    align-items: center;
}

._video_control_left {
    display: flex;
    align-items: center;
}

._video_control_right {
    display: flex;
    align-items: center;
}



._video_control_font {
    font-size: 12px;
    user-select: none;
    color: #fff;
}


/* 播放进度控制*/

._video_control_progress_wrapper {
    width: 100%;
    background-color: #ccc;
    height: 4px;
    position: relative;
    border-radius: 4px;
    cursor: pointer;
}

._video_control_progress {
    width: 10px;
    background-color: #fff;
    height: 4px;
    position: absolute;
    top: 0;
    left: 0;
    border-radius: 4px;
}

._video_control_progress_btn {
    width: 12px;
    height: 12px;
    border-radius: 6px;
    background-color: #fff;
    position: absolute;
    top: -4px;
    left: 0px;
    cursor: pointer;
}


/* 倍速控制*/

._video_control_speed_wrapper {
    position: relative;
}

._video_control_speed_wrapper:hover ._video_control_speed_panel {
    display: block;
}

._video_control_speed_panel {
    position: absolute;
    width: 40px;
    bottom: 30px;
    left: -4px;
    background-color: rgba(0, 0, 0, 0.7);
    border-radius: 8px;
    overflow: hidden;
    display: none;
}


._video_control_speed {
    cursor: pointer;
}

._active {
    background-color: #ccc;
}

/* 音量控制*/
._video_control_voice_compnent {
    position: relative;
    display: flex;
    align-items: center;

    border-radius: 40px;
    padding-left: 20px;
}

._video_control_voice_compnent:hover {
    transition: all 0.5s;
    background-color: rgba(0, 0, 0, 0.6);
}

._video_control_voice_compnent:hover ._video_control_voice_progress_wrapper {
    visibility: visible;
}

._video_control_voice_progress_wrapper {
    height: 4px;
    width: 40px;
    background: #ccc;
    border-radius: 4px;
    position: relative;
    visibility: hidden;
    cursor: pointer;
}

._video_control_voice_progress {
    height: 4px;
    width: 10px;
    background: #fff;
    border-radius: 4px;
    position: absolute;
    top: 0;
    left: 0;
}

._video_control_voice_progress_btn {
    width: 12px;
    height: 12px;
    border-radius: 6px;
    background-color: #fff;
    position: absolute;
    top: -4px;
    left: 4px;
    cursor: pointer;
}

/* 基础按键*/

._video_control_start_wrapper,
._video_control_voice_wrapper,
._video_control_fullscreen_wrapper,
._video_control_speed_wrapper {
    cursor: pointer;
    width: 34px;
    height: 34px;
    margin: 0px 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 500px;
}

._video_control_start_wrapper:hover,
._video_control_fullscreen_wrapper:hover {
    background: rgba(0, 0, 0, 0.6);
}





._video_control_btn {
    display: block;
    width: 14px;
    height: 14px;
    background-repeat: no-repeat;
    background-size: cover;
}

._video_control_play {

    background-image: url('./play.svg');

}

._video_control_pause {
    background-image: url('./pause.svg');
}

._video_control_soundon {
    background-image: url('./sound-on.svg');
}

._video_control_soundoff {
    background-image: url('./sound-off.svg');
}

._video_control_fullscreen {
    background-image: url('./fullscreen-enter.svg');
}

._video_control_origin {
    background-image: url('./fullscreen-exit.svg');
}
function toggleBtnClass(dom, replaceArr) {
    const fliterDom = dom.querySelector("._video_control_btn");
    const cls = fliterDom.classList.toString();
    const res = cls.includes(replaceArr[0])
        ? cls.replace(replaceArr[0], replaceArr[1])
        : cls.replace(replaceArr[1], replaceArr[0]);
    fliterDom.setAttribute("class", res);
    return res;
}

function fullScreen(docElm) {
    if (window.isFullScreen) {
        return;
    }
    // const docElm = document.documentElement
    if (docElm.requestFullscreen) {
        docElm.requestFullscreen();
    } else if (docElm.mozRequestFullScreen) {
        docElm.mozRequestFullScreen();
    } else if (docElm.webkitRequestFullScreen) {
        docElm.webkitRequestFullScreen();
    } else if (docElm.msRequestFullscreen) {
        docElm.msRequestFullscreen();
    }
    window.isFullScreen = true;
}

function cancelFullScreen() {
    if (!window.isFullScreen) {
        return;
    }
    if (document.exitFullscreen) {
        document.exitFullscreen();
    } else if (document.mozCancelFullScreen) {
        document.mozCancelFullScreen();
    } else if (document.webkitCancelFullScreen) {
        document.webkitCancelFullScreen();
    } else if (document.msCancelFullScreenn) {
        document.msCancelFullScreen();
    }
    window.isFullScreen = false;
}

let isMoving = false;

function sliderMove(range, offsetLeft, percent, isMoving) {
    if (isMoving) return;
    // return
    var bar = range.querySelector(".bar");
    var progress = range.querySelector(".progress");
    var dot = range.querySelector(".dot");
    var maximum = 100; // maximum最大值默认为100

    // 总长度减去原点覆盖的部分
    var rest = bar.offsetWidth - dot.offsetWidth;
    let left = (parseFloat(percent) * rest) / maximum;

    if (left < 0) {
        left = 0;
    }
    if (left >= rest) {
        left = rest;
    }
    dot.style.left = left + "px";
    progress.style.width =
        (bar.offsetWidth * Math.ceil(percent)) / maximum + "px";
}

function slider(range, offsetLeft, cb) {
    var bar = range.querySelector(".bar");
    var progress = range.querySelector(".progress");
    var dot = range.querySelector(".dot");
    var maximum = 100; // maximum最大值默认为100
    // 总长度减去原点覆盖的部分
    var rest = bar.offsetWidth - dot.offsetWidth;

    function handelClick(left) {
        isMoving = true;
        if (left < 0) {
            left = 0;
        }
        if (left >= rest) {
            left = rest;
        }
        dot.style.left = left + "px";
        let bili = (left / rest) * maximum;
        cb && cb(Math.ceil(bili));
        progress.style.width = (bar.offsetWidth * Math.ceil(bili)) / maximum + "px";
    }

    // 判断是否移动端
    if (
        !navigator.userAgent.match(
            /(iPhone|iPod|Android|ios|iOS|iPad|Backerry|WebOS|Symbian|Windows Phone|Phone)/i
        )
    ) {
        // 鼠标按下事件
        dot.onmousedown = function (ev) {
            isMoving = true;
            /*
             * offsetLeft 获取的是相对于父对象的左边距, 返回的是数值, 没有单位
             */
            let dotL = dot.offsetLeft;
            let e = ev || window.event; //兼容性
            /*
             * clientX 事件属性返回当事件被触发时鼠标指针向对于浏览器页面(或客户区)的水平坐标。
             */
            let mouseX = e.clientX; //鼠标按下的位置
            window.onmousemove = function (ev) {
                let e = ev || window.event;
                // 浏览器当前位置减去鼠标按下的位置
                let moveL = e.clientX - mouseX; //鼠标移动的距离
                // 保存newL是必要的
                let newL = dotL + moveL; //left值
                // 判断最大值和最小值
                if (newL < 0) {
                    newL = 0;
                }
                if (newL >= rest) {
                    newL = rest;
                }
                // 改变left值
                dot.style.left = newL + "px";
                // 计算比例
                let bili = (newL / rest) * maximum;
                cb && cb(Math.ceil(bili));
                progress.style.width =
                    (bar.offsetWidth * Math.ceil(bili)) / maximum + "px";
                return false; //取消默认事件
            };
            window.onmouseup = function () {
                window.onmousemove = false; //解绑移动事件
                return false;
            };
            return false;
        };
        // 点击进度条
        bar.onclick = (ev) => {
            console.log(ev.clientX, range.offsetLeft, offsetLeft);
            let left =
                ev.clientX - offsetLeft - range.offsetLeft - dot.offsetWidth / 2;
            handelClick(left);
            return false;
        };
    } else {
        // 触摸事件
        dot.ontouchstart = function (ev) {
            isMoving = true;
            let dotL = dot.offsetLeft;
            let e = ev || window.event; //兼容性
            let mouseX = e.touches[0].clientX; //触摸的位置
            window.ontouchmove = function (ev) {
                let e = ev || window.event;
                // 浏览器当前位置减去鼠标按下的位置
                let moveL = e.touches[0].clientX - mouseX; //触摸移动的距离
                // 保存newL是必要的
                let newL = dotL + moveL; //left值
                // 判断最大值和最小值
                if (newL < 0) {
                    newL = 0;
                }
                if (newL >= rest) {
                    newL = rest;
                }
                // 改变left值
                dot.style.left = newL + "px";
                // 计算比例
                let bili = (newL / rest) * maximum;
                cb && cb(Math.ceil(bili));
                progress.style.width =
                    (bar.offsetWidth * Math.ceil(bili)) / maximum + "px";
                return false; //取消默认事件
            };
            window.ontouchend = function () {
                window.ontouchmove = false; //解绑移动事件
                return false;
            };
            return false;
        };
        // 点击进度条
        bar.ontouchstart = function (ev) {
            let left =
                ev.touches[0].clientX -
                offsetLeft -
                range.offsetLeft -
                dot.offsetWidth / 2;
            handelClick(left);
            return false;
        };
    }
}
/**
 * 根据秒数计算时间
 */
function dealTime(time) {
    const minute = Math.floor(time / 60);
    const seconds = Math.floor(time % 60);
    let sec = seconds < 10 ? `0${seconds}` : seconds;
    let minu = "00";
    let hour = "00";
    if (minute >= 60) {
        hour = Math.floor(minute / 60);
        hour = hour < 10 ? `0${hour}` : hour;
        minu = minute % 60;
        minu = minu < 10 ? `0${minu}` : minu;
    } else {
        minu = minute < 10 ? `0${minute}` : minute;
    }
    return `${hour}:${minu}:${sec}`;
}

window.onload = () => {
    const _commonPlayer = document.querySelector("#videoplayer");
    const vdom = _commonPlayer.querySelector("#video");
    vdom.volume = 0.25;
    
    const playDom = _commonPlayer.querySelector("._video_control_start_wrapper");
    const voiceDom = _commonPlayer.querySelector("._video_control_voice_wrapper");
    const fullscreenDom = _commonPlayer.querySelector(
        "._video_control_fullscreen_wrapper"
    );

    const totalTime = _commonPlayer.querySelector(".total_time");
    const currentTime = _commonPlayer.querySelector(".current_time");

    totalTime.innerHTML = dealTime(parseInt(vdom.duration));

    /**
     * 数值为css偏移量, 初始化进度滑块、音量滑块
     */
    slider(
        _commonPlayer.querySelector("._video_control_bottom"),
        _commonPlayer.offsetLeft + 10,
        (num) => {
            const curTime = Math.floor(parseInt((num * vdom.duration) / 100));
            currentTime.innerHTML = dealTime(curTime);
            console.log(
                curTime,
                vdom.duration,
                vdom.duration - curTime < 1 ? vdom.duration : curTime
            );
            vdom.currentTime = curTime;
            if(  vdom.duration - vdom.currentTime < 1){
                vdom.pause();
                const fliterDom = playDom.querySelector("._video_control_btn");
                const cls = fliterDom.classList.toString();
                const res = cls.includes('pause')
                    ? cls.replace('pause', 'play')
                    : cls;
                fliterDom.setAttribute("class", res);
            }
        }
    );
    slider(
        _commonPlayer.querySelector("._video_control_voice_compnent"),
        _commonPlayer.offsetLeft + 20,
        (num) => {
            vdom.volume = num / 100;
            const fliterDom = _commonPlayer.querySelector(
                "._video_control_voice_wrapper ._video_control_btn"
            );
            const cls = fliterDom.classList.toString();
            let res;
            if (vdom.volume === 0) {
                res = cls.replace("soundon", "soundoff");
            } else {
                res = cls.replace("soundoff", "soundon");
            }
            fliterDom.setAttribute("class", res);
        }
    );

    /**
     * 视频监听,时间获取
     */

    let timer = null;
    vdom.addEventListener("playing", () => {
        const duration = vdom.duration;
        timer = setInterval(() => {
            const time = parseInt(vdom.currentTime) + 1;
            currentTime.innerHTML = dealTime(time < duration ? time : duration);
            const percent = parseFloat((vdom.currentTime * 100) / duration).toFixed(
                2
            );
            sliderMove(
                _commonPlayer.querySelector("._video_control_bottom"),
                _commonPlayer.offsetLeft + 10,
                percent
            );
        }, 1000);
    });
    vdom.addEventListener("pause", () => {
        console.log("pause");
        clearInterval(timer);
        timer = null;
    });
    vdom.addEventListener(
        "ended",
        () => {
            console.log("ended");
            clearInterval(timer);
            timer = null;
            const fliterDom = playDom.querySelector("._video_control_btn");
            const cls = fliterDom.classList.toString();
            const res = cls.includes('pause')
                ? cls.replace('pause', 'play')
                : cls;
            fliterDom.setAttribute("class", res);
            
        },
        false
    );

    /**
     * 倍速逻辑
     */
    const ptn = _commonPlayer.querySelector("._video_control_speed_panel");
    ptn.onclick = (e) => {
        const len = e.target.parentElement.children.length;
        for (let i = 0; i < len; i++) {
            e.target.parentElement.children[i].setAttribute("class", "");
        }
        _commonPlayer.querySelector("._video_control_speed").innerHTML =
            e.target.innerHTML;
        vdom.playbackRate = parseFloat(e.target.innerHTML);
        e.target.setAttribute("class", "_active");
    };

    /**
     * 播放暂停逻辑
     */
    playDom.onclick = (e) => {
        if(  vdom.duration - vdom.currentTime < 1){
            vdom.currentTime = 0;
        }
        const res = toggleBtnClass(playDom, ["play", "pause"]);
        
        if (res.includes("play")) {
            vdom.pause();
        } else {
            
            if(vdom.currentTime === 0){
                sliderMove(
                    _commonPlayer.querySelector("._video_control_bottom"),
                    _commonPlayer.offsetLeft + 10,
                    0
                );
            }
            vdom.play();
        }
    };
    /**
     * 静音播放逻辑
     */
    voiceDom.onclick = () => {
        const res = toggleBtnClass(voiceDom, ["soundon", "soundoff"]);
        vdom.volume = res.includes("soundon") ? 0.25 : 0;
        console.log(vdom.volume);
        sliderMove(
            _commonPlayer.querySelector("._video_control_voice_compnent"),
            _commonPlayer.offsetLeft + 20,
            vdom.volume * 100
        );
    };
    /**
     * 全屏取消全屏逻辑
     */
    fullscreenDom.onclick = () => {
        const res = toggleBtnClass(fullscreenDom, ["fullscreen", "origin"]);
        if (res.includes("origin")) {
            fullScreen(_commonPlayer);
        } else {
            cancelFullScreen();
        }
    };
};

最终预览效果:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值