Обновление: поддержка настроек цвета аватара и фона.
Обновление: поддержка вставки чата в режиме реального времени, поддержка потокового макета чата.
В последнее время, поскольку проекту необходимо настроить функцию заграждения, я пытаюсь использовать холст для разработки компонентов. После тестирования на некоторых младших машинах явных задержек нет, поделюсь с вами.
заградительный эффект
Функции
- Поддержка кругового заграждения
- Заграждение не пересекается
- Поддержка выбора количества дорожек
- Поддержка отправки экрана пули
использовать
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-стиль
<template>
<div class="barrage-container">
<canvas
ref="canvasContainer"
:width="barrageWidth"
:height="barrageHeight"
style="display: none;"></canvas>
<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.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
<style lang="scss" scoped>
.barrage-container { // 点击事件穿透
pointer-events: none;
}
.container {
width: 100%;
overflow: hidden;
}
</style>
- другие функции
/**
* 获取随机颜色
*/
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'
})
Эпилог
На этот раз вставленная заграждение отображается сразу, а исходная вставка заграждения появится только в конце списка. Дорожка вставки экрана маркеров также автоматически вставляется сзади после того, как предыдущий экран маркеров появляется на определенном расстоянии, что соответствует формату макета потоковой передачи.
Если эта статья была вам полезна, ставьте лайк!