vue實(shí)現(xiàn)鼠標(biāo)滑動(dòng)預(yù)覽視頻封面組件示例詳解
組件效果


http://www.dbjr.com.cn/Special/926.htm
組件設(shè)計(jì)
我們首先應(yīng)該要對(duì)組件進(jìn)行一個(gè)簡(jiǎn)單的設(shè)計(jì)。

主要的邏輯如上圖??????,可以拆分成這么幾個(gè)步驟:
1、視頻截取關(guān)鍵幀
我們可以先將視頻各個(gè)時(shí)間的關(guān)鍵幀截圖保存,具體截取幀數(shù)可以使用傳入?yún)?shù)控制。
2、鼠標(biāo)移入封面時(shí)顯示對(duì)應(yīng)關(guān)鍵幀
在鼠標(biāo)移入的時(shí)候我們應(yīng)該要計(jì)算當(dāng)前鼠標(biāo)位置和視頻寬度的比例關(guān)系,然后從視頻幀列表中獲取到對(duì)應(yīng)的圖片作為當(dāng)前的視頻封面圖片。
3、視頻和封面的狀態(tài)切換
這里我們是將用兩個(gè)元素分別作為視頻和封面,所以我們狀態(tài)切換的時(shí)候需要控制兩個(gè)元素的顯示和隱藏。
- 點(diǎn)擊封面
顯示并播放視頻,隱藏封面。
- 暫停播放
顯示封面,隱藏視頻。
功能實(shí)現(xiàn)
分析完組件的關(guān)鍵步驟之后我們便可以開(kāi)始動(dòng)手來(lái)實(shí)現(xiàn)相應(yīng)的功能了。
1、視頻截取關(guān)鍵幀圖片列表
1.1 截取指定幀
視頻關(guān)鍵幀的截取我們可以使用canvas來(lái)實(shí)現(xiàn),具體實(shí)現(xiàn)方法如下:
/**
* @param {element} video
* @param {number} currentTime
* @return {void}
*/
cutCover(video, currentTime) {
video.currentTime = currentTime;
const canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = parseInt(this.width);
canvas.height = parseInt(this.height);
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
const img = canvas.toDataURL("image/png");
return img;
},
通過(guò)該函數(shù)我們可以獲取指定時(shí)間的視頻圖片幀。
- 傳入?yún)?shù)
/**
* @param {element} video
* @param {number} currentTime
*/
video為需要截取視頻的dom元素,currentTime為要截取圖片幀的時(shí)間點(diǎn)。
- 返回參數(shù)
/**
* @return {Base64} img
*/
返回參數(shù)為截取的指定幀的Base64格式的圖片。
1.2 截取stepNums張關(guān)鍵幀圖片
stepNums為我們傳入的組件參數(shù),及需要截取的封面關(guān)鍵幀圖片數(shù)量,數(shù)量越多,預(yù)覽的效果越連貫,可以根據(jù)視頻長(zhǎng)度來(lái)調(diào)整截取的張數(shù)。
init(){
const videoContentShow = document.getElementById(
this.uid + "-video"
);
videoContentShow.style.height = this.height;
videoContentShow.style.width = this.width;
const videoContent = videoContentShow.cloneNode();
videoContent.addEventListener("canplay", () => {
if (this.currentTime < this.duration) this.cut(videoContent);
else this.progressValue = 0;
});
}
cut(video) {
const duration = video.duration;
this.duration = duration;
this.currentTime += duration / this.stepNums;
const img = this.cutCover(video, this.currentTime);
this.imgList.push(img);
if (this.imgList.length == 2) {
this.coverSrc = img;
const coverImg = document.getElementById(
this.uid + "-coverImg"
);
coverImg.setAttribute("src", img);
}
}
具體代碼如上,首先我們應(yīng)該先要獲取到視頻的dom元素,但是要注意:我們不在原始視頻元素上進(jìn)行截取操作,我們這里使用了cloneNode()來(lái)克隆一個(gè)dom元素進(jìn)行操作。因?yàn)樵谶M(jìn)行截取的時(shí)候我們需要對(duì)視頻的currentTime屬性進(jìn)行一個(gè)修改,也就是改變視頻的播放進(jìn)度,如果在原視頻上截取的話,在未截取完成前播放視頻會(huì)導(dǎo)致視頻播放進(jìn)度混亂,所以這里我們?cè)诳寺≡貙?duì)象上進(jìn)行操作。
我們總共需要截取stepNums張圖片,所以每次截取的時(shí)間間隔應(yīng)該為:duration / this.stepNums,即視頻總時(shí)間長(zhǎng)度/截取圖片張數(shù),循環(huán)截取即可。
2、鼠標(biāo)移入封面時(shí)顯示對(duì)應(yīng)關(guān)鍵幀
鼠標(biāo)移入封面的時(shí)候我們需要對(duì)封面圖片進(jìn)行切換。
2.1 鼠標(biāo)移動(dòng)事件監(jiān)聽(tīng)
<img
:id="uid + '-coverImg'"
:src="coverSrc"
class="j-coverImg"
@mousemove="imgHover"
@mouseleave="hoverOut"
@click="coverClick"
/>
這里我們使用vue中的mousemove和mouseleave對(duì)鼠標(biāo)事件進(jìn)行監(jiān)聽(tīng)。
imgHover(e) {
const coverImg = document.getElementById(this.uid + "-coverImg");
const w = coverImg.offsetWidth / this.stepNums;
const x = e.offsetX - coverImg.offsetLeft;
const index = Math.min(
Math.max(Math.ceil(x / w), 1),
this.stepNums
);
if (this.imgList.length < index) return;
this.progressValue = index;
coverImg.setAttribute(
"src",
this.imgList[Math.min(this.imgList.length - 1, index)]
);
},
鼠標(biāo)移入的時(shí)候我們需要根據(jù)鼠標(biāo)的坐標(biāo)位置來(lái)計(jì)算展示的幀數(shù)下標(biāo),具體計(jì)算如下:
- 每張圖片展示的區(qū)間大小
const w = coverImg.offsetWidth / this.stepNums;
每個(gè)區(qū)間的大小我們只需要將封面的寬度除于圖片幀列表的數(shù)量即可得到每張圖片展示的區(qū)間大小。
- 當(dāng)前鼠標(biāo)所在區(qū)間
const x = e.offsetX - coverImg.offsetLeft;
const index = Math.min(
Math.max(Math.ceil(x / w), 1),
this.stepNums
);
首先我們應(yīng)該要計(jì)算當(dāng)前鼠標(biāo)在封面里的相對(duì)位置,這里我們只需要其橫坐標(biāo)x即可,然后將坐標(biāo)除于區(qū)間大小,我們即可得到當(dāng)前坐標(biāo)所對(duì)應(yīng)的區(qū)間下標(biāo)。這里的最大值應(yīng)該進(jìn)行限制為1和stepNums。
2.2 鼠標(biāo)移出事件監(jiān)聽(tīng)
鼠標(biāo)移出的時(shí)候我們需要將封面恢復(fù)成當(dāng)前視頻的封面。
hoverOut(e) {
const coverImg = document.getElementById(this.uid + "-coverImg");
const step = this.duration / this.stepNums;
const index = Math.ceil(this.pauseTime / step);
this.progressValue = index;
coverImg.setAttribute("src", this.pauseCover || this.coverSrc);
},
3、視頻和封面的狀態(tài)切換
封面和視頻的顯示隱藏需要根據(jù)播放狀態(tài)來(lái)進(jìn)行對(duì)應(yīng)的切換。
3.1 播放視頻
點(diǎn)擊封面的時(shí)候播放視頻,需要隱藏封面及相關(guān)的進(jìn)度條并顯示視頻
doHide(hide = false) {
const videoContent = document.getElementById(this.uid + "-video");
videoContent.style.display = hide ? "block" : "none";
videoContent.currentTime = this.pauseTime;
hide ? videoContent.play() : videoContent.pause();
const img = document.getElementById(this.uid + "-coverImg");
img.style.display = hide ? "none" : "block";
const progress = document.getElementById(this.uid + "-progress");
progress.style.display = hide ? "none" : "block";
const progress1 = document.getElementById(this.uid + "-progress1");
progress1.style.display = hide ? "none" : "block";
},
coverClick() {
this.doHide(true);
},
3.2 視頻暫停
視頻暫停時(shí)我們需要隱藏視頻,截取當(dāng)前幀作為封面并顯示封面及相關(guān)的進(jìn)度條。
videoContentShow.addEventListener("pause", e => {
this.pauseTime = videoContentShow.currentTime;
this.pauseCover = this.cutCover(
videoContentShow,
videoContentShow.currentTime
);
coverImg.setAttribute("src", this.pauseCover);
const step = this.duration / this.stepNums;
const index = Math.ceil(this.pauseTime / step);
this.progressValue = index;
setTimeout(() => {
if (videoContentShow.paused) this.doHide();
}, 200);
});
這里我使用了一個(gè)setTimeout來(lái)進(jìn)行一個(gè)延時(shí)控制,大家知道為什么嗎?因?yàn)橐曨l有兩種操作會(huì)觸發(fā)視頻的pause事件:
- 點(diǎn)擊暫停按鈕
- 拉動(dòng)進(jìn)度條
這里拉動(dòng)進(jìn)度條的時(shí)候會(huì)觸發(fā)視頻的pause事件并且馬上繼續(xù)播放,所以我們應(yīng)該要過(guò)濾掉這一情況。
組件使用
<template>
<div class="content">
<div class="video-list">
<j-video-cover
class="video"
:videoUrl="videoUrl"
stepNums="40"
></j-video-cover>
</div>
</div>
</template>
<script>
export default {
data() {
return {
videoUrl: require("../../assets/video/202112250058.mp4"),
}
}
}
</script>
組件庫(kù)引用
這里我將這個(gè)組件打包進(jìn)了自己的一個(gè)組件庫(kù),并將其發(fā)布到了npm上,有需要的同學(xué)也可以直接引入該組件進(jìn)行使用。
引入組件代碼
<j-code-height-light :code = "code"
:keyWords = "keyWords"
:color = "color">
</j-code-height-light>
<!-- 注釋 -->
<div class = 'body'>
<div class = 'title'>標(biāo)題</div>
<div class = 'main'>
<span >內(nèi)容</span>
</div>
</div>
/**
* 組件參數(shù)配置如下
*/
props: {
code: {
type: String,
default: ''
},
keyWords:{
type:Array,
default:[
{
value:'關(guān)鍵字1',
color:'顏色1'
},
{
value:'關(guān)鍵字2',
color:'顏色2'
}
]
},
color:{
type: Object,
default: {
keyWord:'orange',//js關(guān)鍵字
varWord:'purple',//js變量
tagWord:'#F9273F',//html標(biāo)簽
strWord:'green',//字符串變量值
attrWord:'green',//html屬性
attrValue:'yellow',//html屬性值
methodkeyWord:'#74759b',//js方法
functionkeyWord:'#2c9678',//自定義函數(shù)
note:'grey'//注釋
}
}
},
methods:{
test(){
console.log('test');
},
testP(p1,p2){
console.log(p1,p2);
}
}引入后即可直接使用。
源碼地址
組件庫(kù)已開(kāi)源,想要查看完整源碼的可以到 gitee 查看,自己也整理了相關(guān)的文檔對(duì)其進(jìn)行了簡(jiǎn)單介紹,具體如下:
組件文檔 http://shouce.jb51.net/vue/single-file-components.html
以上就是vue實(shí)現(xiàn)鼠標(biāo)滑動(dòng)預(yù)覽視頻封面組件示例詳解的詳細(xì)內(nèi)容,更多關(guān)于vue鼠標(biāo)滑動(dòng)視頻封面預(yù)覽的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue+koa2搭建mock數(shù)據(jù)環(huán)境的詳細(xì)教程
這篇文章主要介紹了vue+koa2搭建mock數(shù)據(jù)環(huán)境的方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05
通過(guò)Element ui往頁(yè)面上加一個(gè)分頁(yè)導(dǎo)航條的方法
這篇文章主要介紹了通過(guò)Element ui往頁(yè)面上加一個(gè)分頁(yè)導(dǎo)航條的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05
前端vue?a鏈接下載文件失敗的問(wèn)題(未發(fā)現(xiàn)文件)
這篇文章主要介紹了前端vue?a鏈接下載文件失敗的問(wèn)題(未發(fā)現(xiàn)文件),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09
詳解本地Vue項(xiàng)目請(qǐng)求本地Node.js服務(wù)器的配置方法
本文只針對(duì)自己需要本地模擬接口于是搭建一個(gè)本地node服務(wù)器供自己測(cè)試使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-03-03
webpack項(xiàng)目中使用vite加速的兼容模式詳解
這篇文章主要為大家介紹了webpack項(xiàng)目中使用vite加速的兼容模式示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
Vue引用第三方datepicker插件無(wú)法監(jiān)聽(tīng)datepicker輸入框的值的解決
這篇文章主要介紹了Vue引用第三方datepicker插件無(wú)法監(jiān)聽(tīng)datepicker輸入框的值的解決,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01

