• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    基于Canvas+Vue的弹幕组件的实现

    最近由于项目需要定制化一个弹幕功能,所以尝试使用canvas来开发组件。经过测试在一些低端机的效果也没有明显的卡顿,和大家交流一下

    弹幕效果

     功能介绍

    使用

    npm i vue-barrage

    参数配置 

    name type default desc
    barrageList Array [] 弹幕数据
    speed Number 4 弹幕滚动速度
    loop Boolean true 是否循环滚动
    channels Number 2 弹幕轨道数

    功能实现

    html样式

    <template>
        <div class="barrage-container">
            <div
                class="container"
                :style="{height: barrageHeight/2+'px'}">
                <canvas
                    id="canvas"
                    ref="canvas"
                    :width="barrageWidth"
                    :height="barrageHeight"
                    :style="{'width': barrageWidth/2 + 'px','height': barrageHeight/2 + 'px'}"/>
            </div>
        </div>
    </template>
    

    js实现

    监听数据源

    watch: {
        barrageList (val) {
            if (val.length !== 0) {
                this.initData() // 数据初始化
                this.render() // 开始渲染
            }
        }
    }

    数据初始化

    barrageArray 是存储弹幕数据用的,包括默认弹幕列表和新增弹幕项

    /**
     * 数据初始化
     */
    initData () {
        for (let i = 0; i < this.barrageList.length; i++) { // 此处处理只显示40个字符
            let content = this.barrageList[i].content.length > 40 ? `${this.barrageList[i].content.substring(0, 40)}...` : this.barrageList[i].content
            this.pushMessage(content, this.barrageList[i].color)
        }
    },
    /**
     * 增加数据
     * @param content
     * @param color
     */
    pushMessage (content, color) {
        let position = this.getPosition() // 确定跑道位置
        let x = this.barrageWidth // 初始位置
        let offsetWidth = 0
        for (let i = 0, len = this.barrageArray.length; i < len; i++) {
            let item = this.barrageArray[i]
            if (position === item.position) { // 如果同跑道,则往后排
                offsetWidth += Math.floor(this.ctx.measureText(item.content).width * 3 + 60)
            }
        }
        this.barrageArray.push({
            content: content, // 弹幕内容
            x: x + offsetWidth, // 确定每一条弹幕的初始位置
            originX: x + offsetWidth, // 存储当前弹幕的位置,以便在循环的时候使用
            position: position,
            width: this.ctx.measureText(content).width * 3, // canvas绘制内容宽度
            color: color || this.getColor() // 自定义颜色
        })
    },

    初始化数据需要处理的就是计算当前弹幕的轨道、位置、宽度,以便在 canvas 绘制的时候使用

    绘制 canvas

    /**
     * 渲染
     */
    render () {
        this.ctx.clearRect(0, 0, this.barrageWidth, this.barrageHeight)
        this.ctx.font = '30px Microsoft YaHei'
        this.draw()
        window.requestAnimationFrame(this.render) // 每隔16.6毫秒渲染一次,如果使用setInterval的话在低端机型会有点卡顿
    },
    /**
     * 开始绘制 文字和背景
     */
    draw () {
        for (let i = 0, len = this.barrageArray.length; i < len; i++) {
            let barrage = this.barrageArray[i]
            try {
                barrage.x -= this.speed
                if (barrage.x < -barrage.width - 100) { // 此处判断弹幕消失时机
                    if (i === this.barrageArray.length - 1) { // 最后一条消失时的判断逻辑
                        if (!this.loop) { //如果不是循环弹幕的话就取消绘制 判断是否循环,不循环执行cancelAnimationFrame
                            cancelAnimationFrame(this.render)
                            return
                        }
                        if (this.addArray.length !== 0) { // 此处判断增加弹幕的逻辑
                            this.barrageArray = this.barrageArray.concat(this.addArray)
                            this.addArray = []
                        }
                        for (let j = 0; j < this.barrageArray.length; j++) { // 给每条弹幕的x初始值
                            this.barrageArray[j].x = this.barrageArray[j].originX
                        }
                    }
                }
                if (barrage.x <= 2 * document.body.clientWidth + barrage.width) { // 判断什么时候开始绘制,如果不判断的话会导致弹幕滚动卡顿
                    // 绘制背景
                    this.drawRoundRect(this.ctx, barrage.x - 15, barrage.position - 30, barrage.width + 30, 40, 20, `rgba(0,0,0,0.75)`)
                    // 绘制文字
                    this.ctx.fillStyle = `${barrage.color}`
                    this.ctx.fillText(barrage.content, barrage.x, barrage.position)
                }
            } catch (e) {
                console.log(e)
            }
        }
    },

    此处判断绘制逻辑,包括什么时候取消,弹幕开始绘制判断,弹幕消失判断

    其他函数

    /**
     * 获取文字位置
     * 使用pathWayIndex来确认每一条弹幕所在的轨道
     * 返回距离顶部的距离
     * @TODO此处还可以优化,根据每条轨道的距离来判断下一条弹幕出现位置 
     */
    getPosition () {
        let range = this.channels
        let top = (this.pathWayIndex % range) * 50 + 40
        this.pathWayIndex++
        return top
    },
    /**
     * 获取随机颜色
     */
    getColor () {
        return '#' + ('00000' + (Math.random() * 0x1000000 << 0).toString(16)).slice(-6);
    },
    /**
     * 绘画圆角矩形
     * @param context
     * @param x
     * @param y
     * @param width
     * @param height
     * @param radius
     * @param color
     */
    drawRoundRect (context, x, y, width, height, radius, color) {
        context.beginPath()
        context.fillStyle = color
        context.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 3 / 2)
        context.lineTo(width - radius + x, y)
        context.arc(width - radius + x, radius + y, radius, Math.PI * 3 / 2, Math.PI * 2)
        context.lineTo(width + x, height + y - radius)
        context.arc(width - radius + x, height - radius + y, radius, 0, Math.PI / 2)
        context.lineTo(radius + x, height + y)
        context.arc(radius + x, height - radius + y, radius, Math.PI / 2, Math.PI)
        context.fill()
        context.closePath()
    }

    此处为弹幕服务函数

    使用

    <barrage
        ref="barrage"
        class="barrage"
        :barrage-list="barrageList"
        :speed="speed"
        :loop="loop"
        :channels="channels"/>
        
    import Barrage from 'vue-barrage'
    
    // 弹幕数据初始化  
    this.barrageList = [{
        content: '试数据测试数测试数据数测试数据',
        color: 'white'
    }]
    
    // 新增弹幕
    this.$refs.barrage.add({
        content: '增加一条新的弹幕增加一条新的弹幕', color: 'white'
    })

    结语

    总的来说这个组件还有可优化的空间,后续我会继续改进。

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    基于Canvas+Vue的弹幕组件的实现 基于,Canvas+Vue,的,弹幕,组件,