您好,登錄后才能下訂單哦!
這篇文章運(yùn)用簡(jiǎn)單易懂的例子給大家介紹canvas如何實(shí)現(xiàn)視頻里的彈幕效果,代碼非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。
最近在著手開發(fā)彈幕視頻網(wǎng)站,通過html5中的canvas實(shí)現(xiàn)了彈幕的功能。
那么閑言碎語不要講,先說思路后上代碼。
思路:從頁面布局上來說就是將一塊畫布覆蓋在了video標(biāo)簽產(chǎn)生的視頻窗口之上,使用絕對(duì)定位就能實(shí)現(xiàn)了。最重要的就是js控制畫布上彈幕的顯示了,每一個(gè)彈幕都包裝成一個(gè)對(duì)象,對(duì)象包含的屬性有彈幕應(yīng)該出現(xiàn)的時(shí)間,彈幕的顏色,彈幕是否是移動(dòng)的以及彈幕的文本。彈幕對(duì)象擁有方法包含:設(shè)置彈幕的橫縱坐標(biāo),彈幕的移動(dòng)函數(shù)。實(shí)現(xiàn)的原理,在監(jiān)聽視頻開始播放的事件,在視頻開始播放時(shí)生成一個(gè)定時(shí)器,定時(shí)器每隔一個(gè)時(shí)間去遍歷循環(huán)彈幕對(duì)象數(shù)組并根據(jù)對(duì)象的屬性在畫布的適當(dāng)位置上繪制出彈幕,計(jì)時(shí)器中除了繪制彈幕的代碼還有執(zhí)行更新彈幕數(shù)組的代碼。
下圖是彈幕效果截屏
那么下面開始直接上代碼:
(function () { window.onload=function () { var video = document.getElementsByTagName("video")[0] var cav = document.getElementsByTagName("canvas")[0] //設(shè)置常量canvas的高度以及寬度 var cavWidth = 800 var cavHeight = 420 cav.width=cavWidth cav.height=cavHeight var ctx = cav.getContext("2d") //存儲(chǔ)彈幕對(duì)象的數(shù)組 var capObjs = [] var lastItemTime var capHeight = 20 var inputEle = document.getElementsByClassName("caption-input-text")[0] var sendEle = document.getElementsByClassName("caption-sendButton")[0] var colorUl = document.getElementsByClassName("colorItems")[0] var ismoveInputEle = document.getElementsByClassName("caption-input-ismove")[0] //彈幕顏色 var colors=["#fff","#FFCCCC","#CCFFCC","#CCCCFF","#FFFFCC","#CCFFFF"] var selectedColorIndex = 0 var prevPlayTime = 0 //測(cè)試數(shù)據(jù)的數(shù)組 var testArrayCopy = [] var capobjId = 0 //彈幕在畫布中高度可能值組成的數(shù)組 var topObjs = [{blank:true , value : 20 ,index:0}, {blank:true , value : 50 ,index:1}, {blank:true , value : 80 ,index:2}, {blank:true , value : 110 ,index:3}, {blank:true , value : 140 ,index:4}, {blank:true , value : 170 ,index:5}, {blank:true , value : 200 ,index:6}, {blank:true , value : 230 ,index:7}, {blank:true , value : 260 ,index:8}, {blank:true , value : 290 ,index:9}, {blank:true , value : 320 ,index:10}, {blank:true , value : 350 ,index:11}, {blank:true , value : 380 ,index:12}, {blank:true , value : 410 ,index:13}] //test data 測(cè)試數(shù)據(jù) var testArray = [{content:"ABCDEFGHIJKLMNOPQRSTUVWXYZ",time:"1",ismove:false,colorIndex:0}, {content:"233333333333333",time:"2",ismove:true,colorIndex:0}, {content:"干杯,哈哈哈~~~~~~",time:"2",ismove:true,colorIndex:5}, {content:"干杯,哈哈哈~~~~~~",time:"2",ismove:true,colorIndex:4}, {content:"干杯,哈哈哈~~~~~~",time:"2",ismove:true,colorIndex:4}, {content:"干杯,哈哈哈~~~~~~",time:"2",ismove:true,colorIndex:0}, {content:"干杯,哈哈哈~~~~~~",time:"2",ismove:true,colorIndex:0}, {content:"233333333333333",time:"3",ismove:true,colorIndex:0}, {content:"233333333333333",time:"3",ismove:true,colorIndex:0}, {content:"233333333333333",time:"3",ismove:true,colorIndex:0}, {content:"233333333333333",time:"3",ismove:true,colorIndex:0}, {content:"233333333333333",time:"3",ismove:true,colorIndex:0}, {content:"233333333333333",time:"3",ismove:true,colorIndex:0}, {content:"233333333333333",time:"4",ismove:false,colorIndex:0}, {content:"233333333333333",time:"5",ismove:true,colorIndex:4}, {content:"233333333333333",time:"6",ismove:true,colorIndex:2}, {content:"233333333333333",time:"7",ismove:true,colorIndex:2}, {content:"233333333333333",time:"7",ismove:true,colorIndex:2}, {content:"233333333333333",time:"7",ismove:true,colorIndex:2}, {content:"233333333333333",time:"7",ismove:true,colorIndex:2}, {content:"233333333333333",time:"7",ismove:true,colorIndex:2}, {content:"233333333333333",time:"7",ismove:true,colorIndex:2}, {content:"233333333333333",time:"8",ismove:true,colorIndex:0}, {content:"233333333333333",time:"9",ismove:true,colorIndex:0}, {content:"233333333333333",time:"10",ismove:true,colorIndex:0}, {content:"老師說的非常好,我要好好學(xué)習(xí)了》》》》",time:"12",ismove:true,colorIndex:0}, {content:"老師說的非常好,我要好好學(xué)習(xí)了》》》》",time:"13",ismove:true,colorIndex:0}, {content:"老師說的非常好,我要好好學(xué)習(xí)了》》》》",time:"14",ismove:true,colorIndex:2}, {content:"老師說的非常好,我要好好學(xué)習(xí)了》》》》",time:"15",ismove:false,colorIndex:0}, {content:"老師說的非常好,我要好好學(xué)習(xí)了》》》》",time:"16",ismove:true,colorIndex:2}, {content:"老師說的非常好,我要好好學(xué)習(xí)了》》》》",time:"17",ismove:true,colorIndex:3}, {content:"老師說的非常好,我要好好學(xué)習(xí)了》》》》",time:"18",ismove:true,colorIndex:2}, {content:"老師說的非常好,我要好好學(xué)習(xí)了》》》》",time:"19",ismove:true,colorIndex:0}, {content:"老師說的非常好,我要好好學(xué)習(xí)了》》》》",time:"20",ismove:true,colorIndex:3}, {content:"老師說的非常好,我要好好學(xué)習(xí)了》》》》",time:"21",ismove:true,colorIndex:0}, {content:"老師說的非常好,我要好好學(xué)習(xí)了》》》》",time:"22",ismove:true,colorIndex:0}, {content:"老鐵們,小禮物走一波了,小汽車小火箭刷起來吧=========",time:"23",ismove:true,colorIndex:0}, {content:"老鐵們,小禮物走一波了,小汽車小火箭刷起來吧=========",time:"24",ismove:true,colorIndex:0}, {content:"老鐵們,小禮物走一波了,小汽車小火箭刷起來吧=========",time:"25",ismove:true,colorIndex:3}, {content:"老鐵們,小禮物走一波了,小汽車小火箭刷起來吧=========",time:"26",ismove:true,colorIndex:0}, {content:"老鐵們,小禮物走一波了,小汽車小火箭刷起來吧=========",time:"27",ismove:true,colorIndex:5}, {content:"老鐵們,小禮物走一波了,小汽車小火箭刷起來吧=========",time:"28",ismove:false,colorIndex:5}, {content:"老鐵們,小禮物走一波了,小汽車小火箭刷起來吧=========",time:"29",ismove:true,colorIndex:5}, {content:"老鐵們,小禮物走一波了,小汽車小火箭刷起來吧=========",time:"30",ismove:true,colorIndex:5}, {content:"馬上就下課了,瓦羅藍(lán)大陸走起了~~~",time:"31",ismove:true,colorIndex:5}, {content:"馬上就下課了,瓦羅藍(lán)大陸走起了~~~",time:"32",ismove:true,colorIndex:2}, {content:"馬上就下課了,瓦羅藍(lán)大陸走起了~~~",time:"33",ismove:true,colorIndex:2}, {content:"馬上就下課了,瓦羅藍(lán)大陸走起了~~~",time:"33",ismove:true,colorIndex:5}, {content:"馬上就下課了,瓦羅藍(lán)大陸走起了~~~",time:"34",ismove:true,colorIndex:5}, {content:"馬上就下課了,瓦羅藍(lán)大陸走起了~~~",time:"35",ismove:true,colorIndex:5}, {content:"馬上就下課了,瓦羅藍(lán)大陸走起了~~~",time:"36",ismove:true,colorIndex:2}, {content:"馬上就下課了,瓦羅藍(lán)大陸走起了~~~",time:"37",ismove:true,colorIndex:2}] //將測(cè)試數(shù)據(jù)備份 copyArray(testArray , testArrayCopy) /*彈幕對(duì)象的構(gòu)造函數(shù),參數(shù)分別是:1.ismove:彈幕是否是移動(dòng)的彈幕,2.spe:彈幕的移動(dòng)速度,3.col:彈幕的顏色,4.text:彈幕的文本*/ /*原型鏈方法 setTopValue設(shè)置縱坐標(biāo),setLeftValue設(shè)置橫坐標(biāo),moving完成坐標(biāo)的改變,setId完成id值的設(shè)置*/ function Caption( ismove , spe , col , text ) { this.isMove = ismove this.speed = spe this.color = col || "#ff0" this.content = text this.latestTime = 0 this.width = text.length * 20 this.id = 0 this.topIndex = 0 this.occupyPos = true this.top = 300 this.left = 0 this.setLeftValue() this.setTopValue() } Caption.prototype.setTopValue = function () { for(var i = 0 ,len = topObjs.length ; i < len ; i++){ if (topObjs[i].blank) { this.top = topObjs[i].value this.topIndex = i topObjs[i].blank = false break } } } Caption.prototype.setLeftValue = function () { if (this.isMove) { this.left = cavWidth } else { var contentLength = this.content.length var nowItemLeft = 420 - contentLength * 9 this.left = nowItemLeft } } Caption.prototype.moving = function () { if (this.isMove) { this.left-=this.speed if ( this.left + this.width < cavWidth && this.occupyPos) { this.occupyPos = false topObjs[this.topIndex].blank = true } } else{ this.latestTime += 1 if (this.latestTime > 450) { topObjs[this.topIndex].blank = true } } } Caption.prototype.setId = function () { this.id = capobjId capobjId++ } var cap1 = new Caption( false , 1 , 0 , "小禮物走一波,雙擊6666。。。。") capObjs.push(cap1) cap1.setId() //循環(huán)遍歷數(shù)組,根據(jù)對(duì)象的屬性繪制在畫布上 function drawAllText () { ctx.clearRect( 0 , 0 , cavWidth , cavHeight) ctx.beginPath() for(var i=0 , len = capObjs . length ; i < len ; i++ ){ ctx.fillStyle = capObjs[i].color ctx.font = "bold 20px Courier New" ctx.fillText( capObjs[i].content , capObjs[i].left , capObjs[i].top ) ctx.closePath() capObjs[i].moving() // if (capObjs[i].left < - cavWidth ) { // capObjs.splice (i ,1) // if excute this statement , will has fault because some item in array is null // solution is : write a new function to refresh the array // } } } //更新數(shù)組,當(dāng)對(duì)象已經(jīng)超出范圍的時(shí)候從數(shù)組刪除這個(gè)對(duì)象 function refreshObjs(objs) { for (var i = objs.length - 1; i >= 0; i--) { if (objs[i].left < - cavWidth || objs[i].latestTime > 450 ) { objs.splice(i , 1) } } } //更新保存彈幕對(duì)象的數(shù)組 function updateArray () { var now = parseInt( video.currentTime ) for (var i = testArray.length - 1; i >= 0; i--) { var nowItemTime = parseInt(testArray[i].time) if ( nowItemTime == now ) { //首次寫的控制高度的方式,空間利用不充分,后來改為setTopValue中的方式 // var nowItemLeft = getLeftValue(testArray[i]) // var diffTime = Math.abs(nowItemTime - lastItemTime) // if (diffTime < 6) { // capHeight += 30 // capHeight = capHeight > 400 ? 20 : capHeight // } var temcolor = colors[testArray[i].colorIndex] var temcap = new Caption ( testArray[i].ismove , 1 , temcolor , testArray[i].content ) capObjs.push(temcap) capObjs[capObjs.length - 1].setId() temcap = null testArray.splice(i,1) } } } //當(dāng)用戶點(diǎn)擊send發(fā)送彈幕的回調(diào)函數(shù) function sendCaption (argument) { var inputEleTxt = inputEle.value var now = parseInt( video.currentTime ) var inputIsmoveValue = ismoveInputEle.checked var temObj = {content:inputEleTxt,time:now,ismove:inputIsmoveValue,colorIndex:selectedColorIndex} testArray.push(temObj) inputEle.value = "" } // function getLeftValue (obj) { // if (obj.ismove) { // return 0 // } // else { // var contentLength = obj.content.length // var nowItemLeft = 420 - contentLength * 9 // return nowItemLeft // } // } //重新啟動(dòng)canvas,用在人為導(dǎo)致進(jìn)度條時(shí)間的改變 function reinitCav (argument) { // testArray = testArrayCopy copyArray(testArrayCopy , testArray) capObjs = [] capHeight = 0 clearInterval(canvasTimer) canvasTimer = null initCanvas() } var canvasTimer = null //初始化canvas,用在開始播放時(shí) function initCanvas () { if (canvasTimer == null ) { canvasTimer = setInterval(function (argument) { drawAllText() updateArray() refreshObjs(capObjs) },10) } }//end function initCanvas //復(fù)制數(shù)組 function copyArray (arr1 , arr2) { for (var i =0 , len=arr1.length ; i < len ; i++) { arr2[i] = arr1[i] } } //color select event 用戶發(fā)送彈幕的顏色控制代碼 colorUl.addEventListener("click", function( e ){ var prevSelectItemId = "" switch (selectedColorIndex) { case 0: prevSelectItemId = "colorItemFrist" break; case 1: prevSelectItemId = "colorItemSecond" break; case 2: prevSelectItemId = "colorItemThrid" break; case 3: prevSelectItemId = "colorItemFourth" break; case 4: prevSelectItemId = "colorItemFifth" break; case 5: prevSelectItemId = "colorItemSixth" break; default: // statements_def break; } var prevSelectItem = document.getElementById(prevSelectItemId) prevSelectItem.className = "" var eventTarget = e.target eventTarget.className = "selectedColor" var eveTarId = eventTarget.id.substring(9) switch (eveTarId) { case "Frist": selectedColorIndex = 0 break; case "Second": selectedColorIndex = 1 break; case "Thrid": selectedColorIndex = 2 break; case "Fourth": selectedColorIndex = 3 break; case "Fifth": selectedColorIndex = 4 break; case "Sixth": selectedColorIndex = 5 break; default: // statements_def break; } }, false) video.addEventListener("playing" , function () { initCanvas() }) //進(jìn)度條改變執(zhí)行代碼 video.addEventListener("timeupdate", function () { var nowPlayTime = video.currentTime var diffTime = Math.abs(nowPlayTime - prevPlayTime) prevPlayTime = nowPlayTime if (diffTime > 1) { reinitCav() } }, false) //視頻暫停執(zhí)行代碼 video.addEventListener("pause" , function () { clearInterval(canvasTimer) canvasTimer = null }) //點(diǎn)擊send的監(jiān)聽事件 sendEle.addEventListener("click" , sendCaption) //input的回車監(jiān)聽事件 inputEle.addEventListener("keydown", function(e) { var keynum = 0 keynum = window.event ? e.keyCode : e.which if (keynum == 13) { sendCaption() } }) var aaaa = function() { alert(1) } aaaa() // function b(aaaa){ // return aaaa() // } // b() }//end })()
關(guān)于canvas如何實(shí)現(xiàn)視頻里的彈幕效果就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。