前言
沉寂了一周了,打算把这几天的结果呈现给大家。这几天抽空就一直在搞一个自定义视频播放器,为什么会有如此想法?是因为之前看一些学习视频网站时,看到它们做的视频播放器非常Nice!于是,就打算抽空开发一款属于自己的视频播放器。话不多说,一起来实战吧!
项目展示
(这只是一张图片哦~)
这张图就是我们的成品,还等什么?赶紧来实战吧!
实战
我会把完整源码放在github上,欢迎来star,地址在文末。
首先,我们会使用最原生的JavaScript来实现,老大哥肯定要打头阵啊!
一、JavaScript
iconfont.css
:阿里字体图标文件,你可以在上面找到很多漂亮的图标。index.css
:项目样式文件。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类
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>
看到上面的代码,已经发现我们可以配置视频播放器了,那么这个vp.js
到底是何方神圣呢?我们来看下。
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; }; }
这样不仅可以自定义配置一个视频播放器,逻辑文件中的每一个方法函数还非常的简单明了,可以说是达到我们要求的目的了。但是我们可以更简洁。
三、模板字符串
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> </div> </div> `;
我们再进一步,使用Vue.js、React.js分别实现一波。
四、Vue.js
vue@2.6.12
:引入Vue.js,这里我们使用@2.6.12。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>VamVideo(Vue.js版)</title> <link rel="stylesheet" href="./css/iconfont/iconfont.css" /> <link rel="stylesheet" href="./css/index.css" /> </head> <body> <div id="app"> <vam-video></vam-video> </div> <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script> <script src="./js/vp.js"></script> <script> Vue.component("vam-video", { template: ` <div class="video-box" @mouseenter="vp.bottomTup()" @mouseleave="vp.bottomTdow()"> <video class="video-player" @canplay="vp.useOnplay()" @ended="vp.useEnd()"></video> <div class="bottom-tool"> <div class="pv-bar"> <div class="pv-played"></div> <div class="pv-dot" @mousedown="vp.useTime()"></div> </div> <div class="pv-controls" @mouseleave="vp.selectListHide()"> <div class="pc-con-l"> <div class="play-btn" @click="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" @mousedown="vp.useListen()"></p> <p class="pv-bg"></p> </div> <div class="pv-iconyl" @click="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" @mouseover="vp.selectListShow()">1x</p> <ul class="selectList" @click="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" @click="vp.fullScreen()"> <i class="iconfont icon-quanping"></i> <i class="iconfont icon-huanyuan hide"></i> </div> </div> </div> </div> </div> `, }); const vm = new Vue({ el: "#app", }); 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>
从上面的代码中可以看到,可以直接在全局实例化一个对象,可以根据自己的需要进行配置。
五、React.js
react.development.js
- React 的核心库。react-dom.development
- 提供与 DOM 相关的功能。babel.min.js
- Babel 可以将 ES6 代码转为 ES5 代码,这样我们就能在目前不支持 ES6 浏览器上执行 React 代码。Babel 内嵌了对 JSX 的支持。通过将 Babel 和 babel-sublime 包(package)一同使用可以让源码的语法渲染上升到一个全新的水平。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>VamVideo(React.js版)</title> <link rel="stylesheet" href="./css/iconfont/iconfont.css" /> <link rel="stylesheet" href="./css/index.css" /> <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script> <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script> <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script> </head> <body> <div id="app"></div> <script src="./js/vp.js"></script> <script type="text/babel"> class VamVideoPlayer extends React.Component { constructor(props) { super(props); this.vper = null; } vp(v) { this.vper = v; } componentDidMount() { let vps = 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", } ); this.vp(vps); } render() { return ( <div className="video-box" onMouseEnter={() => this.vper.bottomTup()} onMouseLeave={() => this.vper.bottomTdow()} > <video className="video-player" onCanPlay={() => this.vper.useOnplay()} onEnded={() => this.vper.useEnd()} ></video> <div className="bottom-tool"> <div className="pv-bar"> <div className="pv-played"></div> <div className="pv-dot" onMouseDown={(e) => this.vper.useTime(e)} ></div> </div> <div className="pv-controls" onMouseLeave={() => this.vper.selectListHide()} > <div className="pc-con-l"> <div className="play-btn" onClick={() => this.vper.usePlay()} > <i className="iconfont icon-bofang"></i> <i className="iconfont icon-zanting hide"></i> </div> <div className="pv-time"> <span className="pv-currentTime">00:00:00</span> <span>/</span> <span className="pv-duration">00:00:00</span> </div> </div> <div className="pc-con-r"> <div className="pv-listen ml"> <div className="pv-yl"> <p className="pv-ol" onMouseDown={(e) => this.vper.useListen(e)} ></p> <p className="pv-bg"></p> </div> <div className="pv-iconyl" onClick={() => this.vper.useVolume()} > <i className="iconfont icon-yinliang"></i> <i className="iconfont icon-jingyin hide"></i> </div> </div> <div className="pv-speed ml"> <p className="pv-spnum" onMouseOver={(e) => this.vper.selectListShow(e)} > 1x </p> <ul className="selectList" onClick={(e) => this.vper.useSpnum(e)} > <li>0.5x</li> <li>1x</li> <li>1.25x</li> <li>1.5x</li> <li>2x</li> </ul> </div> <div className="pv-screen ml" onClick={() => this.vper.fullScreen()} > <i className="iconfont icon-quanping"></i> <i className="iconfont icon-huanyuan hide"></i> </div> </div> </div> </div> </div> ); } } ReactDOM.render(<VamVideoPlayer />, document.getElementById("app")); </script> </body> </html>
上面React版本可能有点老,但是逻辑不会变。大家可以使用最新版本或者脚手架来开发一个视频播放器组件,这样一切都是自己说了算。
结语
到这里,我们使用五种方法来实践一个自定义配置视频播放器。梦想就这么简单地实现了!你可以查看完整源码到我的github上,地址在这https://github.com/maomincoding/vamPlayer
。
项目中主要难点在于拖拽那块,大家可以先自己尝试着去理解,我将会在下一篇主要讲述本项目所遇到的一些问题以及解决方法。欢迎及时关注我的动态哦~谢谢