搜索
首页web前端H5教程html5使用canvas制作弹幕功能实例讲解

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

最近在着手开发弹幕视频网站,通过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项目地址

以上是html5使用canvas制作弹幕功能实例讲解的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
H5:如何增强网络上的用户体验H5:如何增强网络上的用户体验Apr 19, 2025 am 12:08 AM

H5通过多媒体支持、离线存储和性能优化提升网页用户体验。1)多媒体支持:H5的和元素简化开发,提升用户体验。2)离线存储:WebStorage和IndexedDB允许离线使用,提升体验。3)性能优化:WebWorkers和元素优化性能,减少带宽消耗。

解构H5代码:标签,元素和属性解构H5代码:标签,元素和属性Apr 18, 2025 am 12:06 AM

HTML5代码由标签、元素和属性组成:1.标签定义内容类型,用尖括号包围,如。2.元素由开始标签、内容和结束标签组成,如内容。3.属性在开始标签中定义键值对,增强功能,如。这些是构建网页结构的基本单位。

了解H5代码:HTML5的基本原理了解H5代码:HTML5的基本原理Apr 17, 2025 am 12:08 AM

HTML5是构建现代网页的关键技术,提供了许多新元素和功能。1.HTML5引入了语义化元素如、、等,增强了网页结构和SEO。2.支持多媒体元素和,无需插件即可嵌入媒体。3.表单增强了新输入类型和验证属性,简化了验证过程。4.提供了离线和本地存储功能,提升了网页性能和用户体验。

H5代码:Web开发人员的最佳实践H5代码:Web开发人员的最佳实践Apr 16, 2025 am 12:14 AM

H5代码的最佳实践包括:1.使用正确的DOCTYPE声明和字符编码;2.采用语义化标签;3.减少HTTP请求;4.使用异步加载;5.优化图像。这些实践能提升网页的效率、可维护性和用户体验。

H5:网络标准和技术的发展H5:网络标准和技术的发展Apr 15, 2025 am 12:12 AM

Web标准和技术从HTML4、CSS2和简单的JavaScript演变至今,经历了显着的发展。 1)HTML5引入了Canvas、WebStorage等API,增强了Web应用的复杂性和互动性。 2)CSS3增加了动画和过渡功能,使页面效果更加丰富。 3)JavaScript通过Node.js和ES6的现代化语法,如箭头函数和类,提升了开发效率和代码可读性,这些变化推动了Web应用的性能优化和最佳实践的发展。

H5是HTML5的速记吗?探索细节H5是HTML5的速记吗?探索细节Apr 14, 2025 am 12:05 AM

H5不仅仅是HTML5的简称,它代表了一个更广泛的现代网页开发技术生态:1.H5包括HTML5、CSS3、JavaScript及相关API和技术;2.它提供更丰富、互动、流畅的用户体验,能在多设备上无缝运行;3.使用H5技术栈可以创建响应式网页和复杂交互功能。

H5和HTML5:网络开发中常用的术语H5和HTML5:网络开发中常用的术语Apr 13, 2025 am 12:01 AM

H5与HTML5指的是同一个东西,即HTML5。HTML5是HTML的第五个版本,带来了语义化标签、多媒体支持、画布与图形、离线存储与本地存储等新功能,提升了网页的表现力和交互性。

H5指的是什么?探索上下文H5指的是什么?探索上下文Apr 12, 2025 am 12:03 AM

H5referstoHTML5,apivotaltechnologyinwebdevelopment.1)HTML5introducesnewelementsandAPIsforrich,dynamicwebapplications.2)Itsupportsmultimediawithoutplugins,enhancinguserexperienceacrossdevices.3)SemanticelementsimprovecontentstructureandSEO.4)H5'srespo

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热工具

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具