Rumah hujung hadapan web Tutorial H5 html5使用canvas制作弹幕功能实例讲解

html5使用canvas制作弹幕功能实例讲解

Sep 14, 2017 am 09:43 AM
canvas h5 html5

这篇文章主要介绍了html5使用canvas实现弹幕功能示例的相关资料,需要的朋友可以参考下

最近在着手开发弹幕视频网站,通过html5中的canvas实现了弹幕的功能。

那么闲言碎语不要讲,先说思路后上代码。

思路:从页面布局上来说就是将一块画布覆盖在了video标签产生的视频窗口之上,使用绝对定位就能实现了。最重要的就是js控制画布上弹幕的显示了,每一个弹幕都包装成一个对象,对象包含的属性有弹幕应该出现的时间,弹幕的颜色,弹幕是否是移动的以及弹幕的文本。弹幕对象拥有方法包含:设置弹幕的横纵坐标,弹幕的移动函数。实现的原理,在监听视频开始播放的事件,在视频开始播放时生成一个定时器,定时器每隔一个时间去遍历循环弹幕对象数组并根据对象的属性在画布的适当位置上绘制出弹幕,计时器中除了绘制弹幕的代码还有执行更新弹幕数组的代码。

下图是弹幕效果截屏

那么下面开始直接上代码:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

(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

})()

Salin selepas log masuk

希望能够对想要制作弹幕的同学有所帮助,还可以去github下载完整的项目代码:gitbub项目地址

Atas ialah kandungan terperinci html5使用canvas制作弹幕功能实例讲解. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
2 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
Repo: Cara menghidupkan semula rakan sepasukan
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: Cara mendapatkan biji gergasi
3 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Sempadan Jadual dalam HTML Sempadan Jadual dalam HTML Sep 04, 2024 pm 04:49 PM

Sempadan Jadual dalam HTML

Jadual Bersarang dalam HTML Jadual Bersarang dalam HTML Sep 04, 2024 pm 04:49 PM

Jadual Bersarang dalam HTML

HTML jidar-kiri HTML jidar-kiri Sep 04, 2024 pm 04:48 PM

HTML jidar-kiri

Susun Atur Jadual HTML Susun Atur Jadual HTML Sep 04, 2024 pm 04:54 PM

Susun Atur Jadual HTML

Memindahkan Teks dalam HTML Memindahkan Teks dalam HTML Sep 04, 2024 pm 04:45 PM

Memindahkan Teks dalam HTML

Senarai Tertib HTML Senarai Tertib HTML Sep 04, 2024 pm 04:43 PM

Senarai Tertib HTML

Butang onclick HTML Butang onclick HTML Sep 04, 2024 pm 04:49 PM

Butang onclick HTML

Pemegang Tempat Input HTML Pemegang Tempat Input HTML Sep 04, 2024 pm 04:54 PM

Pemegang Tempat Input HTML

See all articles