社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
前言
沉寂了一周了,打算把这几天的结果呈现给大家。这几天抽空就一直在搞一个自定义视频播放器,为什么会有如此想法?是因为之前看一些学习视频网站时,看到它们做的视频播放器非常Nice!于是,就打算抽空开发一款属于自己的视频播放器。话不多说,一起来实战吧!
项目展示
(这只是一张图片哦~)
这张图就是我们的成品,还等什么?赶紧来实战吧!
实战
我会把完整源码放在github上,欢迎来star,地址在文末。
首先,我们会使用最原生的JavaScript来实现,老大哥肯定要打头阵啊!
一、JavaScript
1.iconfont.css:阿里字体图标文件,你可以在上面找到很多漂亮的图标。
2.index.css:项目样式文件。
3.index.js:项目逻辑文件。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>VamVideo(原生js版)</title>
- <link rel="stylesheet" href="./css/iconfont/iconfont.css" />
- <link rel="stylesheet" href="./css/index.css" />
- </head>
- <body>
- <div class="video-box">
- <video class="video-player" preload="auto" poster="./img/bg.png">
- <source
- src="https://mos-vod-drcn.dbankcdn.cn/P_VT/video_injection/A91343E9D/v3/9AB0A7921049102362779584128/MP4Mix_H.264_1920x1080_6000_HEAAC1_PVC_NoCut.mp4"
- />
- <source />
- </video>
- <div class="bottom-tool">
- <div class="pv-bar">
- <div class="pv-played"></div>
- <div class="pv-dot"></div>
- </div>
- <div class="pv-controls">
- <div class="pc-con-l">
- <div class="play-btn">
- <i class="iconfont icon-bofang"></i>
- <i class="iconfont icon-zanting hide"></i>
- </div>
- <div class="pv-time">
- <span class="pv-currentTime">00:00:00</span>
- <span>/</span>
- <span class="pv-duration">00:00:00</span>
- </div>
- </div>
- <div class="pc-con-r">
- <div class="pv-listen ml">
- <div class="pv-yl">
- <p class="pv-ol"></p>
- <p class="pv-bg"></p>
- </div>
- <div class="pv-iconyl">
- <i class="iconfont icon-yinliang"></i>
- <i class="iconfont icon-jingyin hide"></i>
- </div>
- </div>
- <div class="pv-speed ml">
- <p class="pv-spnum">1x</p>
- <ul class="selectList">
- <li>0.5x</li>
- <li>1x</li>
- <li>1.25x</li>
- <li>1.5x</li>
- <li>2x</li>
- </ul>
- </div>
- <div class="pv-screen ml">
- <i class="iconfont icon-quanping"></i>
- <i class="iconfont icon-huanyuan hide"></i>
- </div>
- </div>
- </div>
- </div>
- </div>
- <script src="./js/index.js"></script>
- </body>
- </html>
我们主要看下逻辑文件index.js。
- let timer = null;
- let disX = 0;
- let disL = 0;
- function $(el) {
- return document.querySelector(el);
- }
- function showEl(el) {
- $(el).style.display = "block";
- }
- function hideEl(el) {
- $(el).style.display = "none";
- }
- function setVp(w, h) {
- $(".video-player").style.width = w + "px";
- $(".video-player").style.height = h + "px";
- $(".video-box").style.width = w + "px";
- $(".video-box").style.height = h + "px";
- $(".pv-bar").style.width = w + "px";
- }
- // 时间格式化
- function changeTime(iNum) {
- let iN = parseInt(iNum);
- const iH = toZero(Math.floor(iN / 3600));
- const iM = toZero(Math.floor((iN % 3600) / 60));
- const iS = toZero(Math.floor(iN % 60));
- return iH + ":" + iM + ":" + iS;
- }
- // 整0处理
- function toZero(num) {
- if (num <= 9) {
- return "0" + num;
- } else {
- return "" + num;
- }
- }
- // 底部控制栏
- $(".video-box").onmouseenter = function () {
- $(".bottom-tool").style.bottom = "0px";
- };
- $(".video-box").onmouseleave = function () {
- $(".bottom-tool").style.bottom = "-45px";
- };
- // 倍速播放栏(显示/隐藏)
- $(".pv-spnum").onmouseover = function () {
- showEl(".selectList");
- };
- $(".pv-controls").onmouseleave = function () {
- hideEl(".selectList");
- };
- // 播放/暂停
- $(".play-btn").onclick = function () {
- if ($(".video-player").paused) {
- $(".video-player").play();
- hideEl(".icon-bofang");
- showEl(".icon-zanting");
- nowTime();
- timer = setInterval(nowTime, 1000);
- } else {
- $(".video-player").pause();
- showEl(".icon-bofang");
- hideEl(".icon-zanting");
- clearInterval(timer);
- }
- };
- // 总时长
- $(".video-player").oncanplay = function () {
- $(".pv-duration").innerHTML = changeTime($(".video-player").duration);
- };
- // 播放结束
- $(".video-player").onended = function (params) {
- showEl(".icon-bofang");
- hideEl(".icon-zanting");
- };
- // 播放时长
- function nowTime() {
- $(".pv-currentTime").innerHTML = changeTime($(".video-player").currentTime);
- let scale = $(".video-player").currentTime / $(".video-player").duration;
- let w = $(".pv-bar").offsetWidth - $(".pv-dot").offsetWidth;
- $(".pv-dot").style.left = scale * w + "px";
- $(".pv-played").style.width = scale * w + "px";
- }
- // 静音/取消静音
- $(".pv-iconyl").onclick = function () {
- if ($(".video-player").muted) {
- $(".video-player").volume = 1;
- hideEl(".icon-jingyin");
- showEl(".icon-yinliang");
- $(".video-player").muted = false;
- } else {
- $(".video-player").volume = 0;
- showEl(".icon-jingyin");
- hideEl(".icon-yinliang");
- $(".video-player").muted = true;
- }
- };
- let isfullScreen = false;
- // 全屏
- $(".pv-screen").onclick = function () {
- const w = document.documentElement.clientWidth || document.body.clientWidth;
- const h = document.documentElement.clientHeight || document.body.clientHeight;
- isfullScreen = !isfullScreen;
- if (isfullScreen) {
- setVp(w, h);
- hideEl(".icon-quanping");
- showEl(".icon-huanyuan");
- } else {
- setVp(900, 480);
- showEl(".icon-quanping");
- hideEl(".icon-huanyuan");
- }
- };
- // 播放进度条
- $(".pv-dot").onmousedown = function (ev) {
- let ev1 = ev || window.event;
- disX = ev1.clientX - $(".pv-dot").offsetLeft;
- document.onmousemove = function (ev) {
- let ev2 = ev || window.event;
- let L = ev2.clientX - disX;
- if (L < 0) {
- L = 0;
- } else if (L > $(".pv-bar").offsetWidth - $(".pv-dot").offsetWidth) {
- L = $(".pv-bar").offsetWidth - $(".pv-dot").offsetWidth;
- }
- $(".pv-dot").style.left = L + "px";
- let scale = L / ($(".pv-bar").offsetWidth - $(".pv-dot").offsetWidth);
- $(".video-player").currentTime = scale * $(".video-player").duration;
- nowTime();
- };
- document.onmouseup = function () {
- document.onmousemove = null;
- };
- return false;
- };
- // 音量控制
- $(".pv-ol").onmousedown = function (ev) {
- let ev1 = ev || window.event;
- disL = ev1.clientX - $(".pv-ol").offsetLeft;
- document.onmousemove = function (ev) {
- let ev2 = ev || window.event;
- let L = ev2.clientX - disL;
- if (L < 0) {
- L = 0;
- } else if (L > $(".pv-yl").offsetWidth - $(".pv-ol").offsetWidth) {
- L = $(".pv-yl").offsetWidth - $(".pv-ol").offsetWidth;
- }
- $(".pv-ol").style.left = L + "px";
- let scale = L / ($(".pv-yl").offsetWidth - $(".pv-ol").offsetWidth);
- $(".pv-bg").style.width = $(".pv-ol").offsetLeft + "px";
- if ($(".pv-ol").offsetLeft !== 0) {
- showEl(".icon-yinliang");
- hideEl(".icon-jingyin");
- } else {
- showEl(".icon-jingyin");
- hideEl(".icon-yinliang");
- }
- $(".video-player").volume = scale;
- };
- document.onmouseup = function () {
- document.onmousemove = null;
- };
- return false;
- };
- // 播放速度
- $(".selectList").onclick = function (e) {
- let ev = e || window.event;
- hideEl(".selectList");
- $(".pv-spnum").innerText = ev.target.innerText;
- const value = ev.target.innerText.replace("x", "");
- $(".video-player").playbackRate = value;
- };
这样写是可以实现一个视频播放器,你可以通过改样式文件还有部分逻辑文件来实现一个自定义配置视频播放器,但是这种效果不太好,所以我们将通过使用Es6中的Class类来重写这个自定义配置视频播放器。
二、Class类
1.vp.js:class类逻辑文件。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>VamVideo(Class类版)</title>
- <link rel="stylesheet" href="./css/iconfont/iconfont.css" />
- <link rel="stylesheet" href="./css/index.css" />
- </head>
- <body>
- <div class="video-box" onmouseenter="vp.bottomTup()" onmouseleave="vp.bottomTdow()">
- <video class="video-player" oncanplay="vp.useOnplay()" onended="vp.useEnd()"></video>
- <div class="bottom-tool">
- <div class="pv-bar">
- <div class="pv-played"></div>
- <div class="pv-dot" onmousedown="vp.useTime()"></div>
- </div>
- <div class="pv-controls" onmouseleave="vp.selectListHide()">
- <div class="pc-con-l">
- <div class="play-btn" onclick="vp.usePlay()">
- <i class="iconfont icon-bofang"></i>
- <i class="iconfont icon-zanting hide"></i>
- </div>
- <div class="pv-time">
- <span class="pv-currentTime">00:00:00</span>
- <span>/</span>
- <span class="pv-duration">00:00:00</span>
- </div>
- </div>
- <div class="pc-con-r">
- <div class="pv-listen ml">
- <div class="pv-yl">
- <p class="pv-ol" onmousedown="vp.useListen()"></p>
- <p class="pv-bg"></p>
- </div>
- <div class="pv-iconyl" onclick="vp.useVolume()">
- <i class="iconfont icon-yinliang"></i>
- <i class="iconfont icon-jingyin hide"></i>
- </div>
- </div>
- <div class="pv-speed ml">
- <p class="pv-spnum" onmouseover="vp.selectListShow()">1x</p>
- <ul class="selectList" onclick="vp.useSpnum()">
- <li>0.5x</li>
- <li>1x</li>
- <li>1.25x</li>
- <li>1.5x</li>
- <li>2x</li>
- </ul>
- </div>
- <div class="pv-screen ml" onclick="vp.fullScreen()">
- <i class="iconfont icon-quanping"></i>
- <i class="iconfont icon-huanyuan hide"></i>
- </div>
- </div>
- </div>
- </div>
- </div>
- <script src="./js/vp.js"></script>
- <script>
- const vp = new VamVideo(
- document.querySelector(".video-box"), // 挂载父节点
- { // 视频属性
- poster:"./img/bg.png",
- src:"https://mos-vod-drcn.dbankcdn.cn/P_VT/video_injection/A91343E9D/v3/9AB0A7921049102362779584128/MP4Mix_H.264_1920x1080_6000_HEAAC1_PVC_NoCut.mp4",
- preload:"auto",
- // loop:"loop",
- // autoplay:"autoplay"
- },
- { // 视频样式
- width:"1200px",
- height:"600px"
- }
- );
- </script>
- </body>
- </html>
- class VamVideo {
- constructor(vp, attrObj, styleObj) {
- this.timer = null;
- this.disX = 0;
- this.disL = 0;
- this.isfullScreen = false;
- for (const key in attrObj) {
- if (Object.hasOwnProperty.call(attrObj, key)) {
- this.$(".video-player").setAttribute(key, attrObj[key]);
- }
- }
- for (const key in styleObj) {
- if (Object.hasOwnProperty.call(styleObj, key)) {
- this.$(".video-box").style[`${key}`] = styleObj[key];
- key === "width"
- ? (this.vbw = styleObj.width)
- : (this.vbw = vp.offsetWidth);
- key === "height"
- ? (this.vbh = styleObj.height)
- : (this.vbh = vp.offsetHeight);
- }
- }
- }
- $ = (el) => document.querySelector(el);
- showEl = (el) => {
- this.$(el).style.display = "block";
- };
- hideEl = (el) => {
- this.$(el).style.display = "none";
- };
- setVp = (w, h) => {
- const _w = String(w).indexOf("px") != -1 ? w : w + "px";
- const _h = String(h).indexOf("px") != -1 ? h : h + "px";
- this.$(".video-player").style.width = _w;
- this.$(".video-player").style.height = _h;
- this.$(".video-box").style.width = _w;
- this.$(".video-box").style.height = _h;
- this.$(".pv-bar").style.width = _w;
- };
- nowTime = () => {
- this.$(".pv-currentTime").innerHTML = this.changeTime(
- this.$(".video-player").currentTime
- );
- let scale =
- this.$(".video-player").currentTime / this.$(".video-player").duration;
- let w = this.$(".pv-bar").offsetWidth - this.$(".pv-dot").offsetWidth;
- this.$(".pv-dot").style.left = scale * w + "px";
- this.$(".pv-played").style.width = scale * w + "px";
- };
- changeTime = (iNum) => {
- let iN = parseInt(iNum);
- const iH = this.toZero(Math.floor(iN / 3600));
- const iM = this.toZero(Math.floor((iN % 3600) / 60));
- const iS = this.toZero(Math.floor(iN % 60));
- return iH + ":" + iM + ":" + iS;
- };
- toZero = (num) => {
- if (num <= 9) {
- return "0" + num;
- } else {
- return "" + num;
- }
- };
- // 底部控制栏(显示/隐藏)
- bottomTup = () => {
- this.$(".bottom-tool").style.bottom = "0px";
- };
- bottomTdow = () => {
- this.$(".bottom-tool").style.bottom = "-45px";
- };
- // 倍速播放栏(显示/隐藏)
- selectListShow = () => {
- this.showEl(".selectList");
- };
- selectListHide = () => {
- this.hideEl(".selectList");
- };
- // 播放/暂停
- usePlay = () => {
- if (this.$(".video-player").paused) {
- this.$(".video-player").play();
- this.hideEl(".icon-bofang");
- this.showEl(".icon-zanting");
- this.nowTime();
- this.timer = setInterval(this.nowTime, 1000);
- } else {
- this.$(".video-player").pause();
- this.showEl(".icon-bofang");
- this.hideEl(".icon-zanting");
- clearInterval(this.timer);
- }
- };
- // 总时长
- useOnplay = () => {
- this.$(".pv-duration").innerHTML = this.changeTime(
- this.$(".video-player").duration
- );
- };
- // 播放结束
- useEnd = () => {
- this.showEl(".icon-bofang");
- this.hideEl(".icon-zanting");
- };
- // 静音
- useVolume = () => {
- if (this.$(".video-player").muted) {
- this.$(".video-player").volume = 1;
- this.hideEl(".icon-jingyin");
- this.showEl(".icon-yinliang");
- this.$(".video-player").muted = false;
- } else {
- this.$(".video-player").volume = 0;
- this.showEl(".icon-jingyin");
- this.hideEl(".icon-yinliang");
- this.$(".video-player").muted = true;
- }
- };
- // 全屏
- fullScreen = () => {
- const w = document.documentElement.clientWidth || document.body.clientWidth;
- const h =
- document.documentElement.clientHeight || document.body.clientHeight;
- this.isfullScreen = !this.isfullScreen;
- if (this.isfullScreen) {
- this.setVp(w, h);
- this.hideEl(".icon-quanping");
- this.showEl(".icon-huanyuan");
- } else {
- console.log("w" + this.vbw, "h" + this.vbh);
- this.setVp(this.vbw, this.vbh);
- this.showEl(".icon-quanping");
- this.hideEl(".icon-huanyuan");
- }
- };
- // 播放进度条
- useTime = (ev) => {
- let ev1 = ev || window.event;
- this.disX = ev1.clientX - this.$(".pv-dot").offsetLeft;
- document.onmousemove = (ev) => {
- let ev2 = ev || window.event;
- let L = ev2.clientX - this.disX;
- if (L < 0) {
- L = 0;
- } else if (
- L >
- this.$(".pv-bar").offsetWidth - this.$(".pv-dot").offsetWidth
- ) {
- L = this.$(".pv-bar").offsetWidth - this.$(".pv-dot").offsetWidth;
- }
- this.$(".pv-dot").style.left = L + "px";
- let scale =
- L / (this.$(".pv-bar").offsetWidth - this.$(".pv-dot").offsetWidth);
- this.$(".video-player").currentTime =
- scale * this.$(".video-player").duration;
- this.nowTime();
- };
- document.onmouseup = function () {
- document.onmousemove = null;
- };
- return false;
- };
- // 音量控制
- useListen = (ev) => {
- let ev1 = ev || window.event;
- this.disL = ev1.clientX - this.$(".pv-ol").offsetLeft;
- document.onmousemove = (ev) => {
- let ev2 = ev || window.event;
- let L = ev2.clientX - this.disL;
- if (L < 0) {
- L = 0;
- } else if (
- L >
- this.$(".pv-yl").offsetWidth - this.$(".pv-ol").offsetWidth
- ) {
- L = this.$(".pv-yl").offsetWidth - this.$(".pv-ol").offsetWidth;
- }
- this.$(".pv-ol").style.left = L + "px";
- let scale =
- L / (this.$(".pv-yl").offsetWidth - this.$(".pv-ol").offsetWidth);
- this.$(".pv-bg").style.width = this.$(".pv-ol").offsetLeft + "px";
- if (this.$(".pv-ol").offsetLeft !== 0) {
- this.showEl(".icon-yinliang");
- this.hideEl(".icon-jingyin");
- } else {
- this.showEl(".icon-jingyin");
- this.hideEl(".icon-yinliang");
- }
- this.$(".video-player").volume = scale;
- };
- document.onmouseup = function () {
- document.onmousemove = null;
- };
- return false;
- };
- // 播放速度
- useSpnum = (e) => {
- let ev = e || window.event;
- this.hideEl(".selectList");
- this.$(".pv-spnum").innerText = ev.target.innerText;
- const value = ev.target.innerText.replace("x", "");
- this.$(".video-player").playbackRate = value;
- };
- }
这样不仅可以自定义配置一个视频播放器,逻辑文件中的每一个方法函数还非常的简单明了,可以说是达到我们要求的目的了。但是我们可以更简洁。
三、模板字符串
1.strvp.js:把标签语句放在了模板字符串中。
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>VamVideo(模板字符版)</title>
- <link rel="stylesheet" href="./css/iconfont/iconfont.css" />
- <link rel="stylesheet" href="./css/index.css" />
- </head>
- <body>
- <!-- 挂载点 -->
- <div id="app"></div>
- <script src="./js/strvp.js"></script>
- <script src="./js/vp.js"></script>
- <script>
- const node = document.querySelector("#app");
- node.insertAdjacentHTML("beforeEnd", strHtml);
- const vp = new VamVideo(
- document.querySelector(".video-box"), // 挂载父节点
- { // 视频属性
- poster:"./img/bg.png",
- src:"https://mos-vod-drcn.dbankcdn.cn/P_VT/video_injection/A91343E9D/v3/9AB0A7921049102362779584128/MP4Mix_H.264_1920x1080_6000_HEAAC1_PVC_NoCut.mp4",
- preload:"auto",
- // loop:"loop",
- // autoplay:"autoplay"
- },
- { // 视频样式
- width:"1200px",
- height:"600px"
- }
- );
- </script>
- </body>
- </html>
可以看到上面的代码,我直接把标签语句转换为字符串直接挂载到父节点上,这样就更加简洁了。下面的代码就是一堆标签语句。
- const strHtml = `
- <div class="video-box" onmouseenter="vp.bottomTup()" onmouseleave="vp.bottomTdow()">
- <video class="video-player" oncanplay="vp.useOnplay()" onended="vp.useEnd()"></video>
- <div class="bottom-tool">
- <div class="pv-bar">
- <div class="pv-played"></div>
- <div class="pv-dot" onmousedown="vp.useTime()"></div>
- </div>
- <div class="pv-controls" onmouseleave="vp.selectListHide()">
- <div class="pc-con-l">
- <div class="play-btn" onclick="vp.usePlay()">
- <i class="iconfont icon-bofang"></i>
- <i class="iconfont icon-zanting hide"></i>
- </div>
- <div class="pv-time">
- <span class="pv-currentTime">00:00:00</span>
- <span>/</span>
- <span class="pv-duration">00:00:00</span>
- </div>
- </div>
- <div class="pc-con-r">
- <div class="pv-listen ml">
- <div class="pv-yl">
- <p class="pv-ol" onmousedown="vp.useListen()"></p>
- <p class="pv-bg"></p>
- </div>
- <div class="pv-iconyl" onclick="vp.useVolume()">
- <i class="iconfont icon-yinliang"></i>
- <i class="iconfont icon-jingyin hide"></i>
- </div>
- </div>
- <div class="pv-speed ml">
- <p class="pv-spnum" onmouseover="vp.selectListShow()">1x</p>
- <ul class="selectList" onclick="vp.useSpnum()">
- <li>0.5x</li>
- <li>1x</li>
- <li>1.25x</li>
- <li>1.5x</li>
- <li>2x</li>
- </ul>
- </div>
- <div class="pv-screen ml" onclick="vp.fullScreen()">
- <i class="iconfont icon-quanping"></i>
- <i class="iconfont icon-huanyuan hide"></i>
- </div>
- </div>
- </div>
版权声明:本文来源51CTO,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:http://developer.51cto.com/art/202012/636091.htm
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!