当前位置: 首页 > news >正文

canvas滚动 vue_基于Canvas+Vue的弹幕组件

更新:支持头像和背景色设置

更新:支持实时弹幕插入,支持弹幕的流式布局

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

弹幕效果

功能介绍

支持循环弹幕

弹幕不重叠

支持选择轨道数

支持弹幕发送

使用

npm i vue-barrage

参数配置

name

type

default

desc

barrageList

Array

[]

弹幕数据

speed

Number

4

弹幕滚动速度

loop

Boolean

true

是否循环滚动

channels

Number

2

弹幕轨道数

borderColor

String

'#000'

弹幕边框

background

String

'#FFF'

弹幕背景色

功能实现

html样式

ref="canvasContainer"

:width="barrageWidth"

:height="barrageHeight"

style="display: none;">

class="container"

:style="{height: barrageHeight/2+'px'}"

>

id="canvas"

ref="canvas"

:width="barrageWidth"

:height="barrageHeight"

:style="{'width': barrageWidth/2 + 'px',

'height': barrageHeight/2 + 'px'}"

/>

复制代码

js实现

监听数据源

watch: {

barrageList (val) {

if (val.length !== 0) {

this.barrageQueue = val // 将数据保存在更新队列

this.initData() // 数据初始化

window.requestAnimationFrame(this.render) // 开始渲染

}

}

}

复制代码数据初始化

barrageQueue是需要初始化的数组(包括原始数据或者原始数据加新增数据)

waitArray是原始数据加增加的弹幕数据

barrageArray存储未出现的弹幕数据,改数组数据不断减少

/**

* 数据初始化

*/

initData () {

for (let i = 0; i < this.barrageQueue.length; i++) { // 此处处理只显示55个字符

let content = this.barrageQueue[i].content.length > 55 ? `${this.barrageQueue[i].content.substring(0, 55)}...` : this.barrageQueue[i].content

this.barrageArray.push({

content: content, // 初始化内容

x: this.barrageWidth, // 初始弹幕出现位置

width: this.ctx1.measureText(content).width * 3, // 文字宽度

color: this.barrageQueue[i].color || this.getColor()

})

}

this.initChannel()

},

/**

* 初始化轨道数据

* 为每一条轨道初始化一条数据

*/

initChannel () {

for (let i = 0; i < this.channels; i++) {

let item = this.barrageArray.shift()

this.waitArray.push(item)

if (item) {

this.channelsArray[i] = [item]

} else {

this.channelsArray[i] = []

}

}

}

复制代码初始化数据需要处理的就是计算当前弹幕的轨道、位置、宽度,以便在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; i < this.channelsArray.length; i++) {

for (let j = 0; j < this.channelsArray[i].length; j++) {

try {

let barrage = this.channelsArray[i][j]

barrage.x -= this.speed

if (barrage.x <= this.barrageWidth) {

this.drawRoundRect(this.ctx, barrage.x - 15, i * 46 + 8, barrage.width + 30, 40, 20, `rgba(0,0,0,0.75)`)

this.ctx.fillStyle = `${barrage.color}`

this.ctx.fillText(barrage.content, barrage.x, i * 46 + 39)

}

if (barrage.x < -(barrage.width + this.barrageWidth)) { // 弹幕超过一定距离就删除

let item = this.channelsArray[i].shift()

item.x = this.barrageWidth

if (this.loop) { // 弹幕循环处理

let arr = this.channelsArray.reduce((a, b) => a.concat(b))

if (arr.length === 0) {

this.barrageQueue = []

this.barrageQueue = this.waitArray

this.waitArray = []

this.initData()

}

}

}

// 插入弹幕的时机

if (barrage.x <= (this.barrageWidth - barrage.width - 50) && barrage.x >= (this.barrageWidth - barrage.width - 50 - this.speed) && (j === this.channelsArray[i].length - 1) && this.barrageArray.length !== 0) {

let item = this.barrageArray.shift()

this.channelsArray[i].push(item)

this.waitArray.push(item)

}

} catch (e) {

console.log(e)

}

}

}

}

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

新增弹幕

/**

* 重置数据

*/

add (obj) {

let item = {

content: obj.content,

x: this.barrageWidth,

width: this.ctx1.measureText(obj.content).width * 3,

color: obj.color || this.getColor()

}

this.barrageArray.unshift(item)

},

复制代码新增弹幕时将在未出现的弹幕数组首部添加一条数据

CSS

.barrage-container { // 点击事件穿透

pointer-events: none;

}

.container {

width: 100%;

overflow: hidden;

}

复制代码其他函数

/**

* 获取随机颜色

*/

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()

}

复制代码此处为弹幕服务函数

使用

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'

})

复制代码

结语

这一次改版做了插入弹幕立即显示,原来的弹幕插入只会出现在列表的最后。弹幕的插入轨道也是上一条弹幕出现一定距离后自动插入到后面,符合流式布局的格式。

如果这篇文章对你有帮助,不妨点个赞吧!

相关文章:

  • http 阮一峰_JavaScript 标准参考教程(alpha) 阮一峰
  • python时间字符串表示_python – 将pandas datetime month转换为字符串表示形式
  • react实现汉堡_reactjs – 所有顶级屏幕标题(使用抽屉)标题中的react-navigation汉堡包图标?...
  • c# 多线程界面卡顿_C#多线程解决程序卡顿问题
  • 安卓吸顶+下拉放大_【Android】打造下拉放大效果
  • react 父子传值_react 父子组件传值——父传子
  • python和mysql匹配吗_Python最佳实践和最安全的方法来连接MySQL和执行查询
  • java openresty 调用_OpenResty 究竟解决了什么痛点?
  • plsq如何快捷整理代码_plsql常用快捷键
  • php strpose_PHP之strpos
  • win10装debian 双系统_技术|如何拥有一个 Windows 10 和 Debian 10 的双系统
  • redistemplate怎么修改数据_redisTemplate一opsForValue操作
  • linux命令deploy_Linux deploy 超详细入门教程
  • word文档怎么到下一页去写_word文档怎么把下一页的内容移到上一页?
  • 绞车拆装实训报告_千斤顶实训报告.doc
  • JavaScript对象详解
  • JavaScript新鲜事·第5期
  • Java精华积累:初学者都应该搞懂的问题
  • learning koa2.x
  • Mysql5.6主从复制
  • node 版本过低
  • Python 基础起步 (十) 什么叫函数?
  • 产品三维模型在线预览
  • 分布式事物理论与实践
  • 基于Vue2全家桶的移动端AppDEMO实现
  • 开源中国专访:Chameleon原理首发,其它跨多端统一框架都是假的?
  • 码农张的Bug人生 - 见面之礼
  • 深度学习中的信息论知识详解
  • 线性表及其算法(java实现)
  • 详解NodeJs流之一
  • 项目实战-Api的解决方案
  • 学习笔记DL002:AI、机器学习、表示学习、深度学习,第一次大衰退
  • ​七周四次课(5月9日)iptables filter表案例、iptables nat表应用
  • #、%和$符号在OGNL表达式中经常出现
  • #NOIP 2014# day.2 T2 寻找道路
  • #pragma once
  • (30)数组元素和与数字和的绝对差
  • (C语言)strcpy与strcpy详解,与模拟实现
  • (LNMP) How To Install Linux, nginx, MySQL, PHP
  • (pojstep1.1.2)2654(直叙式模拟)
  • (第二周)效能测试
  • (二)斐波那契Fabonacci函数
  • (分布式缓存)Redis分片集群
  • (附源码)python旅游推荐系统 毕业设计 250623
  • (七)c52学习之旅-中断
  • (三分钟了解debug)SLAM研究方向-Debug总结
  • (数据结构)顺序表的定义
  • (一)80c52学习之旅-起始篇
  • (转)【Hibernate总结系列】使用举例
  • (转)AS3正则:元子符,元序列,标志,数量表达符
  • (转)树状数组
  • .gitignore文件---让git自动忽略指定文件
  • .NET 8.0 中有哪些新的变化?
  • .Net core 6.0 升8.0
  • .NET Core Web APi类库如何内嵌运行?