從0到1搭建一款Vue可配置視訊播放器元件(Npm已釋出)
前言
話不多說,這篇文章主要講述如何從0到1搭建一款適用於Vue.js的自定義配置視訊播放器。我們平時在PC端網站上觀看視訊時,會看到有很多豐富樣式的視訊播放器,而我們自己寫的video標籤樣式卻是那麼醜。其實像那些網站都是基於原生video標籤進行開發的,只不過還得適當加工一下,才會有我們所看到的漂亮的視訊播放器。
開發
在具體開發之前,我們需要明確我們需要做什麼?
- 封裝一個可配置的視訊播放器;
- 適用於Vue.js;
- 應用於PC端網站;
- 視訊播放器常用的功能必須要有;
- 釋出到Npm;
好,明確了以上幾點之後,我們就開始敲程式碼了。
一、搭建一個基礎的UI元件
這裡的UI元件你可以理解成我們搭建一個靜態頁面,就是把視訊播放器簡單地搭建起來,有一個基礎的模型。
<template>
<div
class="video-box"
>
<video
class="video-player"
></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 class="pv-screens ml">
<i class="iconfont icon-shipinquanping"></i>
<i class="iconfont icon-tuichuquanping hide"></i>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "VamVideo"
};
</script>
<style scoped>
@import "./css/iconfont/iconfont.css";
@import "./css/index.css";
</style>
樣式檔案我這裡就不展示了,我會在文末給出原始碼地址。
二、開發邏輯執行檔案
最最關鍵的部分莫過於邏輯檔案了,我這裡使用建構函式的方式。
// eslint-disable-next-line no-unused-vars
function VamVideo(vp, attrObj, styleObj) {
// 初始化
this.timer = null;
this.disX = 0;
this.disL = 0;
this.isPageFullScreen = false;
// 處理視訊屬性
for (const key in attrObj) {
if (Object.hasOwnProperty.call(attrObj, key) && key !== "controls") {
$(".video-player").setAttribute(key, attrObj[key]);
}
}
// 處理視訊樣式
for (const key in styleObj) {
if (Object.hasOwnProperty.call(styleObj, key)) {
$(".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);
}
}
// 封裝獲取元素節點
function $(el) {
return document.querySelector(el);
}
// 處理當前時間
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";
}
// 處理時分秒
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;
}
}
// 元素顯示
this.showEl = function (el) {
$(el).style.display = "block";
};
// 元素隱藏
this.hideEl = function (el) {
$(el).style.display = "none";
};
// 動態設定視訊寬高
this.setVp = function (w, h) {
const _w = String(w).indexOf("px") != -1 ? w : w + "px";
const _h = String(h).indexOf("px") != -1 ? h : h + "px";
$(".video-player").style.width = _w;
$(".video-player").style.height = _h;
$(".video-box").style.width = _w;
$(".video-box").style.height = _h;
$(".pv-bar").style.width = _w;
};
// 底部控制欄(顯示/隱藏)
this.bottomTup = function () {
$(".bottom-tool").style.bottom = "0px";
};
this.bottomTdow = function () {
$(".bottom-tool").style.bottom = "-45px";
};
// 播放/暫停
this.usePlay = function () {
if ($(".video-player").paused) {
$(".video-player").play();
this.hideEl(".icon-bofang");
this.showEl(".icon-zanting");
nowTime();
this.timer = setInterval(nowTime, 1000);
} else {
$(".video-player").pause();
this.showEl(".icon-bofang");
this.hideEl(".icon-zanting");
clearInterval(this.timer);
}
};
this.isplay = function () {
this.usePlay();
};
// 總時長
this.useOnplay = function () {
$(".pv-duration").innerHTML = changeTime($(".video-player").duration);
};
// 播放結束
this.useEnd = function () {
this.showEl(".icon-bofang");
this.hideEl(".icon-zanting");
};
// 靜音
this.useVolume = function () {
if ($(".video-player").muted) {
$(".video-player").volume = 1;
this.hideEl(".icon-jingyin");
this.showEl(".icon-yinliang");
$(".video-player").muted = false;
} else {
$(".video-player").volume = 0;
this.showEl(".icon-jingyin");
this.hideEl(".icon-yinliang");
$(".video-player").muted = true;
}
};
// 頁面全屏
this.pageFullScreen = function () {
const w = document.documentElement.clientWidth || document.body.clientWidth;
const h =
document.documentElement.clientHeight || document.body.clientHeight;
this.isPageFullScreen = !this.isPageFullScreen;
if (this.isPageFullScreen) {
this.setVp(w, h);
this.hideEl(".icon-quanping");
this.showEl(".icon-huanyuan");
this.hideEl(".pv-screens");
} else {
this.setVp(this.vbw, this.vbh);
this.showEl(".icon-quanping");
this.hideEl(".icon-huanyuan");
this.showEl(".pv-screens");
}
};
// 視窗全屏
this.fullScreen = function () {
const el = $(".video-box");
const isFullscreen =
document.fullScreen ||
document.mozFullScreen ||
document.webkitIsFullScreen;
if (!isFullscreen) {
this.showEl(".icon-tuichuquanping");
this.hideEl(".icon-shipinquanping");
this.hideEl(".pv-screen");
(el.requestFullscreen && el.requestFullscreen()) ||
(el.mozRequestFullScreen && el.mozRequestFullScreen()) ||
(el.webkitRequestFullscreen && el.webkitRequestFullscreen()) ||
(el.msRequestFullscreen && el.msRequestFullscreen());
} else {
this.showEl(".icon-shipinquanping");
this.hideEl(".icon-tuichuquanping");
this.showEl(".pv-screen");
document.exitFullscreen
? document.exitFullscreen()
: document.mozCancelFullScreen
? document.mozCancelFullScreen()
: document.webkitExitFullscreen
? document.webkitExitFullscreen()
: "";
}
};
// 播放進度條
this.useTime = function (ev) {
let ev1 = ev || window.event;
this.disX = ev1.clientX - $(".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 > $(".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;
};
// 音量控制
this.useListen = function (ev) {
let ev1 = ev || window.event;
this.disL = ev1.clientX - $(".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 > $(".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) {
this.showEl(".icon-yinliang");
this.hideEl(".icon-jingyin");
} else {
this.showEl(".icon-jingyin");
this.hideEl(".icon-yinliang");
}
$(".video-player").volume = scale;
};
document.onmouseup = function () {
document.onmousemove = null;
};
return false;
};
// 播放速度
this.useSpnum = function (e) {
let ev = e || window.event;
$(".pv-spnum").innerText = ev.target.innerText;
const value = ev.target.innerText.replace("x", "");
$(".video-player").playbackRate = value;
};
}
// 匯出
export default VamVideo;
三、整合元件邏輯
開發完UI元件以及邏輯元件了,那我們接下來就是將兩者結合起來。
<template>
<div
class="video-box"
@mouseenter="vp.bottomTup()"
@mouseleave="vp.bottomTdow()"
>
<video
class="video-player"
@canplay="vp.useOnplay()"
@ended="vp.useEnd()"
@click="vp.isplay()"
></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">
<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">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.pageFullScreen()">
<i class="iconfont icon-quanping"></i>
<i class="iconfont icon-huanyuan hide"></i>
</div>
<div class="pv-screens ml" @click="vp.fullScreen()">
<i class="iconfont icon-shipinquanping"></i>
<i class="iconfont icon-tuichuquanping hide"></i>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import VamVideo from "./vp.js";
export default {
name: "VamVideo",
data: () => ({
vp: null,
defaultStyle: {
width: "1200px",
height: "600px",
},
}),
props: {
properties: {
type: Object,
},
videoStyle: {
type: Object,
},
},
mounted() {
this.vp = new VamVideo(
document.querySelector(".video-box"),
this.properties,
Object.keys(this.videoStyle).length === 0
? this.defaultStyle
: this.videoStyle
);
},
};
</script>
<style scoped>
@import "./css/iconfont/iconfont.css";
@import "./css/index.css";
</style>
首先我們引入了之前開發完成的邏輯檔案vp.js
,然後在mounted方法中對類VamVideo
進行例項化,賦給this.vp
。傳給類的幾個引數分別是最外層節點、視訊屬性、視屏樣式。
props屬性中的properties
為視訊屬性,videoStyle
為視訊樣式。
四、釋出元件
完成了以上幾個步驟的開發,我們需要將我們完成的元件釋出到Npm上。
1. 初始化
建立一個空資料夾,我們可以取名叫v-vamvideo。在此資料夾下鍵入命令:
npm init -y
因為我們還需要修改,所以直接建立package.json
檔案。
{
"name": "vue-vam-video",
"version": "1.0.0",
"description": "Vue.js Custom video components",
"main": "index.js",
"author": "maomincoding",
"keywords": ["video"],
"license": "ISC",
"private": false
}
name
:元件名author
:Npm使用者名稱main
:入口檔案version
:版本號,更新元件需要用到這個欄位description
:描述license
的值按照以上即可keywords為
:搜尋的關鍵詞private
設為false
, 開源因此需要將這個欄位改為false
2. 引入元件
將我們之前封裝好的元件複製到v-vamvide這個資料夾中,下圖就是我們之前封裝好的元件檔案目錄。
3. 建立入口檔案
我們要釋出到Npm上需要一個入口檔案,我們在v-vamvide根目錄下建立一個入口檔案,我們這裡叫做index.js
。
// 引入元件
import VamVideo from "./VamVideo/vamvideo.vue";
// 元件需要新增name屬性,代表註冊的元件名稱
VamVideo.install = (Vue) => Vue.component(VamVideo.name, VamVideo); //註冊元件
export default VamVideo;
4. 建立一個說明文件
釋出到Npm上,使用者需要知道這個元件幹什麼的?怎麼用?我們在v-vamvide根目錄下建立一個說明文件,取名為README.md
# vue-vamvideo
> Vue.js Custom video components
## Using documents
1. Introducing components
2. configuration parameter
- `properties`: Video properties.
- `videoStyle`: Video style.
These two parameters need to be set separately.
***
<template>
<div id="app">
<vam-video :properties="videoOption.properties" :videoStyle="videoOption.videoStyle"></vam-video>
</div>
</template>
<script>
export default {
name: "Index",
data: () => ({
videoOption: {
properties: {
poster: require("./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",
// muted:true,
// controls:"controls"
},
videoStyle: {
// width: "1200px",
// height: "600px",
},
},
})
};
</script>
***
我們離成功很近了,所以謝謝你可以閱讀到這。
原始碼地址:https://github.com/maomincoding/vue-vam-video
5. 釋出
開始操作以下步驟之前,你需要把命令列切換到專案根目錄下(也就是這裡的v-vamvide這個資料夾)。
- 檢視登入源是否是
http://registry.npmjs.org
npm config get registry
如果不是,切換登入源。
npm config set registry=http://registry.npmjs.org
- 登入Npm
npm login
相繼輸入使用者名稱、密碼、郵箱。回車出現Logged in as maomincoding on http://registry.npmjs.org
,那麼就登入成功了。
- 上傳發布到Npm
npm publish
五、安裝元件
既然我們已經發布到Npm上,我們可以去Npm網站上看下效果。
https://www.npmjs.com/package/vue-vam-video
釋出元件成功了,那麼我們放在Vue工程上測試一下。
- 安裝元件
npm i vue-vam-video
- 註冊元件
全域性註冊
import Vue from 'vue'
import App from './App.vue'
// 全域性註冊
import VamVideo from "vue-vam-video";
Vue.use(VamVideo);
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
<template>
<div id="app">
<vam-video :properties="videoOption.properties" :videoStyle="videoOption.videoStyle"></vam-video>
</div>
</template>
<script>
export default {
name: "App",
data: () => ({
videoOption: {
properties: {
poster: require("./assets/logo.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",
// muted:true,
// controls:"controls"
},
videoStyle: {
// width: "1200px",
// height: "600px",
},
},
})
};
</script>
區域性註冊
<template>
<div id="app">
<vam-video :properties="videoOption.properties" :videoStyle="videoOption.videoStyle"></vam-video>
</div>
</template>
<script>
import VamVideo from 'vue-vam-video'
export default {
name: "App",
data: () => ({
videoOption: {
properties: {
poster: require("./assets/logo.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",
// muted:true,
// controls:"controls"
},
videoStyle: {
// width: "1200px",
// height: "600px",
},
},
}),
components: {
VamVideo
},
};
</script>
- 效果
大功告成!
結語
謝謝閱讀,如果覺得有用,不要忘記一鍵三連哦~
歡迎關注我的公眾號前端歷劫之路
回覆關鍵詞電子書,即可獲取12本前端熱門電子書。
回覆關鍵詞紅寶書第4版,即可獲取最新《JavaScript高階程式設計》(第四版)電子書。
關注公眾號後,點選下方選單即可加我微信,我拉攏了很多IT大佬,建立了一個技術交流、文章分享群,期待你的加入。
作者:Vam的金豆之路
微信公眾號:前端歷劫之路
相關文章
- 詳解從 0 釋出 react 元件到 npm 上React元件NPM
- 教你從0到1搭建小程式音視訊
- 從0到1搭建自己的元件(vue-code-view)庫(下)元件VueView
- 從0到1搭建自己的元件(vue-code-view)庫(上)元件VueView
- 如何釋出一個公共的 vue 元件到 npmVue元件NPM
- 從搭建腳手架到在npm上釋出react元件NPMReact元件
- ?vue元件釋出npm最佳實踐Vue元件NPM
- 如何從0開發一個Vue元件庫併發布到npmVue元件NPM
- 手把手教你釋出一個vue元件到npm上Vue元件NPM
- 輕鬆釋出 react 元件到 npmReact元件NPM
- 從0到1搭建視訊通話系統,我1天就搞定了
- 如何從0開始搭建 Vue 元件庫Vue元件
- 從0搭建Vue3元件庫(三): 元件庫的環境配置Vue元件
- DNSLOG平臺搭建從0到1DNS
- webpack 4 :從0配置到專案搭建Web
- 基於刮刮卡Vue元件釋出一個NPM包Vue元件NPM
- 使用WebApi+Vue3從0到1搭建《許可權管理系統》:二、搭建JWT系統鑑權WebAPIVueJWT
- 搭建自己的 vue 元件庫(三)—— npm 上面釋出自己的元件庫Vue元件NPM
- 釋出你的第一個 React 元件到 npmReact元件NPM
- 從0到1搭建自助分析平臺
- Vue2.0 新手入門 — 從環境搭建到釋出Vue
- 釋出一款npm包幫助理解npmNPM
- Combine 框架,從0到1 —— 3.使用 Subscriber 控制釋出速度框架
- UI元件庫從0到1開發心得UI元件
- 從0到1,一步步開發React的loading元件,併發布到npm上React元件NPM
- 從0到1,短視訊運營究竟該怎麼做?
- 手把手教你封裝 Vue 元件,並使用 npm 釋出封裝Vue元件NPM
- Combine 框架,從0到1 —— 5.Combine 提供的釋出者(Publishers)框架
- 從0到1搭建element後臺框架許可權設計與優化框架優化
- 從0搭建Vue3元件庫(二):Monorepo專案搭建Vue元件Mono
- 從 0 到 1 實現一款簡易版 WebpackWeb
- 從0到1開始搭建vue2+element ui 後臺管理系統VueUI
- 從0到1搭建和部署個人部落格
- 從0到1搭建DeltaLake大資料平臺大資料
- 從0到1搭建域名郵件伺服器伺服器
- 手把手從0到1:搭建Kubernetes叢集
- Vue.js從0到1:v-bind指令Vue.js
- Combine 框架,從0到1 —— 2.通過 ConnectablePublisher 控制何時釋出框架