最近在著手開發彈幕視頻網站,通過html5中的canvas實現了彈幕的功能。
那麼閒言碎語不要講,先說思路後上代碼。
思路:從頁面佈局上來說就是將一塊畫布覆蓋在了video標籤產生的視頻窗口之上,使用絕對定位就能實現了。最重要的就是js控制畫布上彈幕的顯示了,每一個彈幕都包裝成一個對象,對象包含的屬性有彈幕應該出現的時間,彈幕的顏色,彈幕是否是移動的以及彈幕的文本。彈幕對象擁有方法包含:設置彈幕的橫縱坐標,彈幕的移動函數。實現的原理,在監聽視頻開始播放的事件,在視頻開始播放時生成一個定時器,定時器每隔一個時間去遍歷循環彈幕對像數組並根據對象的屬性在畫布的適當位置上繪製出彈幕,計時器中除了繪製彈幕的代碼還有執行更新彈幕數組的代碼。
下圖是彈幕效果截屏
那麼下面開始直接上代碼:
(function () { window.onload=function () { var video = document.getElementsByTagName(video)[0] var cav = document.getElementsByTagName(canvas)[0] //設置常量canvas的高度以及寬度var cavWidth = 800 var cavHeight = 420 cav.width=cavWidth cav.height=cavHeight var ctx = cav.getContext(2d) //存儲彈幕對象的數組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 //測試數據的數組var testArrayCopy = [] var capobjId = 0 //彈幕在畫布中高度可能值組成的數組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 測試數據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:老師說的非常好,我要好好學習了》》》》,time:12,ismove:true,colorIndex:0}, {content:老師說的非常好,我要好好學習了》》》》,time:13,ismove:true,colorIndex:0},{content:老師說的非常好,我要好好學習了》》》》 ,time:14,ismove:true,colorIndex:2},{content:老師說的非常好,我要好好學習了》》》》,time:15,ismove:false,colorIndex:0},{content:老師說的非常好,我要好好學習了》》》》,time:16,ismove:true,colorIndex:2},{content:老師說的非常好,我要好好學習了》》》》,time:17 ,ismove:true,colorIndex:3},{content:老師說的非常好,我要好好學習了》》》》,time:18,ismove:true,colorIndex:2},{content:老師說的非常好,我要好好學習了》》》》,time:19,ismove:true,colorIndex:0},{content:老師說的非常好,我要好好學習了》》》》,time:20,ismove:true ,colorIndex:3},{content:老師說的非常好,我要好好學習了》》》》,time:21,ismove:true,colorIndex:0},{content:老師說的非常好,我要好好學習了》》》》,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:馬上就下課了,瓦羅藍大陸走起了~~~,time:31,ismove:true,colorIndex :5},{content:馬上就下課了,瓦羅藍大陸走起了~~~,time:32,ismove:true,colorIndex:2},{content:馬上就下課了,瓦羅藍大陸走起了~~~,time:33,ismove:true,colorIndex:2},{content:馬上就下課了,瓦羅藍大陸走起了~~~,time:33,ismove:true,colorIndex:5}, {content:馬上就下課了,瓦羅藍大陸走起了~~~,time:34,ismove:true,colorIndex:5},{content:馬上就下課了,瓦羅藍大陸走起了~~~ ,time:35,ismove:true,colorIndex:5},{content:馬上就下課了,瓦羅藍大陸走起了~~~,time:36,ismove:true,colorIndex:2},{content:馬上就下課了,瓦羅藍大陸走起了~~~,time:37,ismove:true,colorIndex:2}] //將測試數據備份copyArray(testArray , testArrayCopy) /*彈幕對象的構造函數,參數分別是:1.ismove:彈幕是否是移動的彈幕,2.spe:彈幕的移動速度,3.col:彈幕的顏色,4.text:彈幕的文本*/ /*原型鏈方法setTopValue設置縱坐標,setLeftValue設置橫坐標,moving完成坐標的改變,setId完成id值的設置*/ 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() //循環遍歷數組,根據對象的屬性繪製在畫布上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 // } } } //更新數組,當對像已經超出範圍的時候從數組刪除這個對象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) } } } //更新保存彈幕對象的數組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) } } } //當用戶點擊send發送彈幕的回調函數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 // } // } //重新啟動canvas,用在人為導致進度條時間的改變function reinitCav (argument) { // testArray = testArrayCopy copyArray(testArrayCopy , testArray) capObjs = [] capHeight = 0 clearInterval(canvasTimer) canvasTimer = null initCanvas() } var canvasTimer = null //初始化canvas,用在開始播放時function initCanvas () { if (canvasTimer == null ) { canvasTimer = setInterval(function (argument) { drawAllText() updateArray() refreshObjs(capObjs) },10) } } //end function initCanvas //複製數組function copyArray (arr1 , arr2) { for (var i =0 , len=arr1.length ; i < len ; i++) { arr2[i] = arr1[i] } } // color select event 用戶發送彈幕的顏色控制代碼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() } ) //進度條改變執行代碼video.addEventListener(timeupdate, function () { var nowPlayTime = video.currentTime var diffTime = Math.abs(nowPlayTime - prevPlayTime) prevPlayTime = nowPlayTime if (diffTime > 1) { reinitCav() } } , false) //視頻暫停執行代碼video.addEventListener(pause , function () { clearInterval(canvasTimer) canvasTimer = null }) //點擊send的監聽事件sendEle.addEventListener(click , sendCaption) //input的回車監聽事件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})()
希望能夠對想要製作彈幕的同學有所幫助,還可以去github下載完整的項目代碼:gitbub項目地址
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。