說(shuō)起彈幕看過(guò)視頻的都不會(huì)陌生,那滿屏充滿著飄逸評(píng)論的效果,讓人如癡如醉,無(wú)法自拔
最近也是因?yàn)樵趯W(xué)習(xí)關(guān)于 canvas 的知識(shí),所以今天就想和大家分享一個(gè)關(guān)于彈幕的故事
那么究竟彈幕是怎樣煉成的呢? 我們且往下看(look)
看什么?看效果
效果圖已經(jīng)呈現(xiàn)給各位了,那么是不是有點(diǎn)小激動(dòng)呢?是的,感慨萬(wàn)分,思緒寧亂,無(wú)語(yǔ)凝噎
無(wú)論以后我們的工作中是否會(huì)遇到這樣的需求,也請(qǐng)給自己一個(gè)增加技能的機(jī)會(huì)吧??!
本次彈幕的效果,項(xiàng)目結(jié)構(gòu)如下圖所示
項(xiàng)目整體已經(jīng)給出,那么我們就擼起袖子加油干吧。
讓彈幕飛
上面我們提到了 canvas 的事情,所以呢,這就是制作彈幕了。我們利用 canvas 繪圖來(lái)實(shí)現(xiàn)彈幕的功能
首先,我們先給出html的結(jié)構(gòu)
// index.html文件// 引入index.js文件用來(lái)實(shí)現(xiàn)彈幕功能聽(tīng)媽媽的話 - 周杰倫
結(jié)構(gòu)相對(duì)來(lái)說(shuō)沒(méi)什么高級(jí)的內(nèi)容,主要就是寫上了 canvas 標(biāo)簽還有 video 標(biāo)簽,他們才是視頻網(wǎng)站彈幕的絕佳拍檔
那么不再賣關(guān)子了,趕緊進(jìn)行主要活動(dòng)吧
模擬數(shù)據(jù)
// index.js文件 let data = [ {value: "周杰倫的聽(tīng)媽媽的話,讓我反復(fù)循環(huán)再循環(huán)", time: 5, color: "red", speed: 1, fontSize: 22}, {value: "想快快長(zhǎng)大,才能保護(hù)她", time: 10, color: "#00a1f5", speed: 1, fontSize: 30}, {value: "聽(tīng)媽媽的話吧,晚點(diǎn)再戀愛(ài)吧!愛(ài)呢?", time: 15}, ];
數(shù)據(jù)里代表了什么:
value:代表彈幕的內(nèi)容 (必填)
time:代表彈幕展現(xiàn)的時(shí)間 (必填)
color:代表彈幕文字的顏色
speed:代表彈幕飄過(guò)的速度
fontSize:代表彈幕文字的大小
opacity:代表彈幕文字的透明度
除了彈幕的內(nèi)容和展現(xiàn)的時(shí)間外,其他都是可選的,模擬的數(shù)據(jù)里沒(méi)有這些參數(shù)也沒(méi)關(guān)系的
獲取 dom 元素
// index.js文件 // 模擬數(shù)據(jù) ...省略 // 獲取到所有需要的dom元素 let doc = document; let canvas = doc.getElementById("canvas"); let video = doc.getElementById("video"); let $txt = doc.getElementById("text"); let $btn = doc.getElementById("btn"); let $color = doc.getElementById("color"); let $range = doc.getElementById("range");
Canvas渲染彈幕
下面我們將用面向?qū)ο蟮姆绞絹?lái)實(shí)現(xiàn)canvas繪制彈幕的功能,之所以選擇用這種方式主要是方便復(fù)用和后續(xù)添加方法;
下面我們先來(lái)創(chuàng)建一個(gè)CanvasBarrage類,主要用做canvas來(lái)渲染整個(gè)彈幕;
在實(shí)現(xiàn)之前,我們先來(lái)調(diào)用一下,看看是如何創(chuàng)建實(shí)例的。
// index.js文件 // 模擬數(shù)據(jù) ...省略 // 獲取到所有需要的dom元素 ...省略 // 創(chuàng)建CanvasBarrage類 class CanvasBarrage { // todo } // 創(chuàng)建CanvasBarrage實(shí)例 let canvasBarrage = new CanvasBarrage(canvas, video, { data });
實(shí)現(xiàn) CanvasBarrage
我們?cè)凇暗玫剿械膹椖幌ⅰ蹦抢?,通過(guò)數(shù)組的 map 方法返回的還是個(gè)數(shù)組,不過(guò)返回的內(nèi)容是一個(gè) Barrage類,這是為什么呢?
還記得之前說(shuō)過(guò)么,用類的好處就是方便擴(kuò)展,后續(xù)再添加方法的話可以直接在該類中添加即可。
所以我們也不推崇直接map方法里直接返回一個(gè){}這種形式
// 不推薦 this.barrages = this.data.map(item => { item });
說(shuō)到這里我們還要先寫一下Barrage這個(gè)類,不然接下來(lái)的console.log(this)會(huì)因?yàn)檎也坏紹arrage類而報(bào)錯(cuò)
// index.js文件 ++++++++++++++++++++++ // 創(chuàng)建Barrage類,用來(lái)實(shí)例化每一個(gè)彈幕元素 class Barrage { constructor(obj, ctx) { // todo } } ++++++++++++++++++++++ class CanvasBarrage { ...省略}
Now,通過(guò)上面代碼中的console.log(this),我們可以看到,所有掛載到this實(shí)例上的屬性和原型上的方法都呈現(xiàn)眼前了
render 一下
接著上面的 CanvasBarrage 類里 render 方法繼續(xù)寫,我們來(lái)把 todo 完成
todo都做了什么?
1、清除之前畫布所有的繪制,防止繪制重疊的影響
this.clear()
2、渲染真正的彈幕數(shù)據(jù) (還未實(shí)現(xiàn))
this.renderBarrage()
3、判斷是否繼續(xù)渲染彈幕
this.isPaused為false時(shí)表示為播放狀態(tài)
4、遞歸調(diào)用render
通過(guò)requestAnimationFrame來(lái)遞歸調(diào)用render
要比setInterval這樣的方式好很多
渲染整個(gè)彈幕 render 方法就完成了,那么要繼續(xù)寫了,應(yīng)該是剛才未實(shí)現(xiàn)的 renderBarrage 方法了
But,在此之前,我們要先寫個(gè)別的,它就是Barrage類
因?yàn)檫€需要它來(lái)大顯身手一下呢,每一個(gè)彈幕的實(shí)例都由它來(lái)制造
創(chuàng)建 Barrage 類
彈幕制造者來(lái)了,下面我們就來(lái)實(shí)現(xiàn)一下這個(gè)Barrage類,看它都具備哪些屬性和方法,繼續(xù)todo吧
todo都做了什么?
1、從傳入的obj中取到必要的value和time
this.value = obj.value; // 內(nèi)容 this.time = obj.time; // 時(shí)間
2、初始化彈幕
對(duì)每個(gè)彈幕所需的參數(shù)進(jìn)行設(shè)置,如果obj上沒(méi)有,就取默認(rèn)參數(shù)
計(jì)算每個(gè)彈幕的寬度
由于不能直接操縱canvas畫布里的元素,所以先創(chuàng)建一個(gè)p標(biāo)簽
p標(biāo)簽的寬度即為彈幕的寬 -> this.width = p.clientWidt
設(shè)置每個(gè)彈幕的x和y坐標(biāo) (起始位置)
橫向x坐標(biāo)起始位置都是從右邊進(jìn)入,即:畫布的寬度
this.x = this.context.canvas.width
縱向y坐標(biāo)起始位置是不固定的,選在畫布之內(nèi)的任意位置出現(xiàn)
this.y = this.context.canvas.height * Math.random()
處理彈幕超出畫布區(qū)域
canvas是按照字號(hào)基線來(lái)展示字體的,如果小于這個(gè)字號(hào)大小
this.y = this.fontSize
如果大于了畫布高度 - 字號(hào)大小
this.y = this.context.canvas.height - this.fontSize
3、渲染每個(gè)彈幕
繪制文本需要設(shè)置文本的字體字號(hào)、顏色和文本的內(nèi)容與坐標(biāo)
字體字號(hào)api
this.context.ctx.font = ${this.value}px Arial
顏色api
this.context.ctx.fillStyle = this.color
內(nèi)容與坐標(biāo)api
this.context.ctx.fillText(this.value, this.x, this.y)
以上三步就是整個(gè)Barrage類所做的事情了。Barrage這個(gè)類都已經(jīng)敲完了,那么接下來(lái)開始真正的渲染步驟吧
renderBarrage才是主角
此時(shí)我們?cè)偬砑右粋€(gè)觸發(fā)彈幕的事件,讓彈幕飛起來(lái)
大家一起寫到了這里,也是時(shí)候展示一下成果了,往下看
別急,讓彈幕再飛一會(huì)兒
渲染彈幕的功能,我們已經(jīng)完成了,接下來(lái)讓我們馬不停蹄的寫下如何發(fā)彈幕吧。別猶豫,開擼?。?!
發(fā)彈幕
發(fā)彈幕相對(duì)來(lái)說(shuō)還是很簡(jiǎn)單的,獲取到 value, time, color, fontSize 之后把他們當(dāng)作對(duì)象傳給 CanvasBarrage 的 add方法進(jìn)行添加就好了
下面我們?cè)賹懸幌耡dd方法,回到CanvasBarrage類里繼續(xù)寫
// index.js文件 class CanvasBarrage { constructor() { ...省略} render() { ...省略 } renderBarrage() { ...省略 } clear() { ...省略 } +++++++++++++++++++++++++++ add(obj) { // 實(shí)際上就是往barrages數(shù)組里再添加一項(xiàng)Barrage的實(shí)例而已 this.barrages.push(new Barrage(obj, this)); } +++++++++++++++++++++++++++ }
完成,漂亮,看看效果吧
寫到這里我們已經(jīng)完成了視頻網(wǎng)站上的彈幕功能了,可喜可賀
下面我們?cè)賮?lái)完善一下視頻播放時(shí)對(duì)彈幕的播放處理吧
暫停和拖動(dòng)
暫停就停止渲染彈幕
// index.js文件 ...省略 // 播放 video.addEventListener("play", () => { canvasBarrage.isPaused = false; canvasBarrage.render(); }); +++++++++++++++++++++++++++++++++++++++ // 暫停 video.addEventListener("pause", () => { // isPaused設(shè)為true表示暫停播放 canvasBarrage.isPaused = true; }); +++++++++++++++++++++++++++++++++++++++
回放時(shí)需要重新渲染該時(shí)刻的彈幕
// index.js文件 // 暫停 video.addEventListener("pause", () => { canvasBarrage.isPaused = true; }); +++++++++++++++++++++++++++++++++++++++ // 拖動(dòng)進(jìn)度條時(shí)觸發(fā)seeked事件 video.addEventListener("seeked", () => { // 調(diào)用CanvasBarrage類的replay方法進(jìn)行回放,重新渲染彈幕 canvasBarrage.replay(); }); +++++++++++++++++++++++++++++++++++++++
讓我們?cè)俅位氐紺anvasBarrage這個(gè)類上
OK,寫到這里,所有關(guān)于彈幕功能的代碼就全部結(jié)束了??!
如果工作中讓你開發(fā)彈幕功能,你也可以在多敲幾遍以上代碼之后,得心應(yīng)手的保證完成任務(wù)了。
敬請(qǐng)期待,下節(jié)更精彩
接下來(lái)我們?cè)倮?WebSocket 和 redis 來(lái)進(jìn)行一下較為實(shí)戰(zhàn)的功能吧。
作者:chenhongdong
來(lái)源:
https://juejin.im/post/5be54a...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/99996.html
摘要:歡迎訪問(wèn)網(wǎng)易云社區(qū),了解更多網(wǎng)易技術(shù)產(chǎn)品運(yùn)營(yíng)經(jīng)驗(yàn)。網(wǎng)易云易盾智能反垃圾服務(wù),基于網(wǎng)易年專業(yè)的反垃圾經(jīng)驗(yàn),以及積累的億級(jí)別的海量數(shù)據(jù),為客戶提供極速智能的廣告過(guò)濾智能鑒黃暴恐識(shí)別涉政檢測(cè)云服務(wù),助力打造互聯(lián)網(wǎng)純凈內(nèi)容生態(tài)。文章來(lái)源網(wǎng)易云社區(qū) 歡迎訪問(wèn)網(wǎng)易云社區(qū),了解更多網(wǎng)易技術(shù)產(chǎn)品運(yùn)營(yíng)經(jīng)驗(yàn)。 人工審核對(duì)于A站和B站都是很重要的。 AcFun稿件審核通行標(biāo)準(zhǔn)(V1.1) 表明,要審核的東西...
摘要:要達(dá)到水平很高的程序員,第一要找能提供優(yōu)質(zhì)實(shí)踐機(jī)會(huì)的大廠,第二要在諸如高并發(fā)或機(jī)器學(xué)習(xí)的項(xiàng)目里多解決實(shí)際問(wèn)題,第三還要不斷跳槽,不斷地找更優(yōu)質(zhì)的平臺(tái)和更優(yōu)質(zhì)的項(xiàng)目機(jī)會(huì)。 ...
摘要:提高有了入門的基礎(chǔ),開始自學(xué)當(dāng)時(shí)流行的三大框架和。業(yè)余的時(shí)間,經(jīng)常在上閑逛,看一些博客或開源的代碼。 最近有一位小伙伴通過(guò)公眾號(hào)給我留言, 我參加工作沒(méi)多久,看著圈里的技術(shù)大牛,特別羨慕,也渴望成為技術(shù)大牛,想讓您分享一下從小白到大牛是怎樣練成的,我該如何提高自己 首先,謝謝這位小伙伴的一直關(guān)注。其次,我并不是大牛,只是早搬了幾年的磚而已,不過(guò)可以分享一下我的Java開發(fā)之路。 入門 ...
摘要:使用的的方法實(shí)現(xiàn)了文字滾動(dòng)我們需要做一些準(zhǔn)備工作例如我們?yōu)榱俗審椖豢梢宰冾伾覀儗懥讼旅孢@個(gè)方法。判斷存儲(chǔ)彈幕信息的數(shù)據(jù)是否為空隨機(jī)抽取數(shù)組中的一個(gè)元素之后把它追加到這個(gè)中執(zhí)行文字滾動(dòng)的方法。 怎樣才能跑起來(lái)我寫的彈幕程序 資源下載 申請(qǐng)野狗后端云賬號(hào)注冊(cè) 創(chuàng)建應(yīng)用:showImg(https://segmentfault.com/img/remote/146000000683932...
閱讀 1969·2021-11-22 15:29
閱讀 3271·2021-10-14 09:43
閱讀 1236·2021-10-08 10:22
閱讀 3357·2021-08-30 09:46
閱讀 1442·2019-08-30 15:55
閱讀 1938·2019-08-30 15:44
閱讀 861·2019-08-30 14:19
閱讀 1458·2019-08-30 13:13