Sample code of barrage component realized by native JavaScript
- 2021-08-28 19:13:49
- OfStack
Preface
Nowadays, almost all video websites have barrage function, so today we will package a barrage class with native JavaScript. This class wants the following properties and instance methods:
Attribute
Selector of el container node. The container node should be absolutely positioned, and the width and height should be set height Height of each barrage mode barrage mode, half is 1.5 container height, top is 1/3, full is full Time of speed barrage across the screen Distance between the last barrage of gapWidth and the first barrageMethod
pushData Add Barrage Metadata addData continues to join the barrage start starts scheduling barrage stop stop barrage restart restarts barrage clearData Empty Barrage close off open re-display barragePS: If there are 1 self-encapsulated tool function, it will not be posted. It is good to know the meaning probably
Initialization
After introducing the JavaScript file, we want to use it as follows, taking the default configuration first.
let barrage = new Barrage({
el: '#container'
})
Parameter initialization:
function Barrage(options) {
let {
el,
height,
mode,
speed,
gapWidth,
} = options
this.container = document.querySelector(el)
this.height = height || 30
this.speed = speed || 15000 //2000ms
this.gapWidth = gapWidth || 20
this.list = []
this.mode = mode || 'half'
this.boxSize = getBoxSize(this.container)
this.perSpeed = Math.round(this.boxSize.width / this.speed)
this.rows = initRows(this.boxSize, this.mode, this.height)
this.timeoutFuncs = []
this.indexs = []
this.idMap = []
}
Accept the parameters first and then initialize them. Let's look at getBoxSize and initRows
function getBoxSize(box) {
let {
height,
width
} = window.getComputedStyle(box)
return {
height: px2num(height),
width: px2num(width)
}
function px2num(str) {
return Number(str.substring(0, str.indexOf('p')))
}
}
The width and height of the box are calculated by getComputedStyleapi, which is used to calculate the width and height of the container here and will be used later.
function initRows(box, mode, height) {
let divisor = getDivisor(mode)
rows = Math.ceil(box.height * divisor / height)
return rows
}
function getDivisor(mode) {
let divisor = .5
switch (mode) {
case 'half':
divisor = .5
break
case 'top':
divisor = 1 / 3
break;
case 'full':
divisor = 1;
break
default:
break;
}
return divisor
}
Calculate how many lines the barrage should have according to the height, and there will be places to use the number of lines below.
Insert data
There are two ways to insert data, one is to add source data, and the other is to continuously add data. Let's first look at the method of adding source data:
this.pushData = function (data) {
this.initDom()
if (getType(data) == '[object Object]') {
// Insert a single bar
this.pushOne(data)
}
if (getType(data) == '[object Array]') {
// Insert multiple strips
this.pushArr(data)
}
}
this.initDom = function () {
if (!document.querySelector(`${el} .barrage-list`)) {
// Registration dom Node
for (let i = 0; i < this.rows; i++) {
let div = document.createElement('div')
div.classList = `barrage-list barrage-list-${i}`
div.style.height = `${this.boxSize.height*getDivisor(this.mode)/this.rows}px`
this.container.appendChild(div)
}
}
}
this.pushOne = function (data) {
for (let i = 0; i < this.rows; i++) {
if (!this.list[i]) this.list[i] = []
}
let leastRow = getLeastRow(this.list) // Get the least in the barrage list 1 Column, the barrage list is 1 A 2 Dimensional array
this.list[leastRow].push(data)
}
this.pushArr = function (data) {
let list = sliceRowList(this.rows, data)
list.forEach((item, index) => {
if (this.list[index]) {
this.list[index] = this.list[index].concat(...item)
} else {
this.list[index] = item
}
})
}
// According to the number of lines, 1 The barrage of dimension list Segmentation rows OK 2 Dimensional array
function sliceRowList(rows, list) {
let sliceList = [],
perNum = Math.round(list.length / rows)
for (let i = 0; i < rows; i++) {
let arr = []
if (i == rows - 1) {
arr = list.slice(i * perNum)
} else {
i == 0 ? arr = list.slice(0, perNum) : arr = list.slice(i * perNum, (i + 1) * perNum)
}
sliceList.push(arr)
}
return sliceList
}
The method of continuously adding data simply calls the method of adding source data and starts scheduling
this.addData = function (data) {
this.pushData(data)
this.start()
}
Launch barrage
Let's take a look at the logic of firing barrage
this.start = function () {
// Start scheduling list
this.dispatchList(this.list)
}
this.dispatchList = function (list) {
for (let i = 0; i < list.length; i++) {
this.dispatchRow(list[i], i)
}
}
this.dispatchRow = function (row, i) {
if (!this.indexs[i] && this.indexs[i] !== 0) {
this.indexs[i] = 0
}
// The real scheduling starts here, using 1 Instance variables store the subscript of the current schedule.
if (row[this.indexs[i]]) {
this.dispatchItem(row[this.indexs[i]], i, this.indexs[i])
}
}
this.dispatchItem = function (item, i) {
// Scheduled 1 Under a barrage of times 1 You don't need it when you schedule it next time
if (!item || this.idMap[item.id]) {
return
}
let index = this.indexs[i]
this.idMap[item.id] = item.id
let div = document.createElement('div'),
parent = document.querySelector(`${el} .barrage-list-${i}`),
width,
pastTime
div.innerHTML = item.content
div.className = 'barrage-item'
parent.appendChild(div)
width = getBoxSize(div).width
div.style = `width:${width}px;display:none`
pastTime = this.computeTime(width) // Calculate the following 1 When the barrage should appear
// Barrage flight 1 Meeting ~
this.run(div)
if (index > this.list[i].length - 1) {
return
}
let len = this.timeoutFuncs.length
// Record the timer and empty it later
this.timeoutFuncs[len] = setTimeout(() => {
this.indexs[i] = index + 1
// Under recursive call 1 Article
this.dispatchItem(this.list[i][index + 1], i, index + 1)
}, pastTime);
}
// Use css Animation, the whole is relatively smooth
this.run = function (item) {
item.classList += ' running'
item.style.left = "left:100%"
item.style.display = ''
item.style.animation = `run ${this.speed/1000}s linear`
// Completed fight 1 Tags
setTimeout(() => {
item.classList+=' done'
}, this.speed);
}
// According to the width of the barrage and gapWth , work out 1 When the barrage should appear
this.computeTime = function (width) {
let length = width + this.gapWidth
let time = Math.round(length / this.boxSize.width * this.speed/2)
return time
}
The animation css is as follows
function Barrage(options) {
let {
el,
height,
mode,
speed,
gapWidth,
} = options
this.container = document.querySelector(el)
this.height = height || 30
this.speed = speed || 15000 //2000ms
this.gapWidth = gapWidth || 20
this.list = []
this.mode = mode || 'half'
this.boxSize = getBoxSize(this.container)
this.perSpeed = Math.round(this.boxSize.width / this.speed)
this.rows = initRows(this.boxSize, this.mode, this.height)
this.timeoutFuncs = []
this.indexs = []
this.idMap = []
}
0
Remaining methods
Stop
Stop with the paused property of the animation
function Barrage(options) {
let {
el,
height,
mode,
speed,
gapWidth,
} = options
this.container = document.querySelector(el)
this.height = height || 30
this.speed = speed || 15000 //2000ms
this.gapWidth = gapWidth || 20
this.list = []
this.mode = mode || 'half'
this.boxSize = getBoxSize(this.container)
this.perSpeed = Math.round(this.boxSize.width / this.speed)
this.rows = initRows(this.boxSize, this.mode, this.height)
this.timeoutFuncs = []
this.indexs = []
this.idMap = []
}
1
function Barrage(options) {
let {
el,
height,
mode,
speed,
gapWidth,
} = options
this.container = document.querySelector(el)
this.height = height || 30
this.speed = speed || 15000 //2000ms
this.gapWidth = gapWidth || 20
this.list = []
this.mode = mode || 'half'
this.boxSize = getBoxSize(this.container)
this.perSpeed = Math.round(this.boxSize.width / this.speed)
this.rows = initRows(this.boxSize, this.mode, this.height)
this.timeoutFuncs = []
this.indexs = []
this.idMap = []
}
2
Start over
Remove the pause class
this.restart = function () {
let items = document.querySelectorAll(`${el} .barrage-item`);
[...items].forEach(item => {
removeClassName(item, 'pause')
})
}
Open and close
Do 1 show hidden logic can be
function Barrage(options) {
let {
el,
height,
mode,
speed,
gapWidth,
} = options
this.container = document.querySelector(el)
this.height = height || 30
this.speed = speed || 15000 //2000ms
this.gapWidth = gapWidth || 20
this.list = []
this.mode = mode || 'half'
this.boxSize = getBoxSize(this.container)
this.perSpeed = Math.round(this.boxSize.width / this.speed)
this.rows = initRows(this.boxSize, this.mode, this.height)
this.timeoutFuncs = []
this.indexs = []
this.idMap = []
}
4
Clear the barrage
function Barrage(options) {
let {
el,
height,
mode,
speed,
gapWidth,
} = options
this.container = document.querySelector(el)
this.height = height || 30
this.speed = speed || 15000 //2000ms
this.gapWidth = gapWidth || 20
this.list = []
this.mode = mode || 'half'
this.boxSize = getBoxSize(this.container)
this.perSpeed = Math.round(this.boxSize.width / this.speed)
this.rows = initRows(this.boxSize, this.mode, this.height)
this.timeoutFuncs = []
this.indexs = []
this.idMap = []
}
5
Finally, use a timer to regularly clean up the expired barrage:
function Barrage(options) {
let {
el,
height,
mode,
speed,
gapWidth,
} = options
this.container = document.querySelector(el)
this.height = height || 30
this.speed = speed || 15000 //2000ms
this.gapWidth = gapWidth || 20
this.list = []
this.mode = mode || 'half'
this.boxSize = getBoxSize(this.container)
this.perSpeed = Math.round(this.boxSize.width / this.speed)
this.rows = initRows(this.boxSize, this.mode, this.height)
this.timeoutFuncs = []
this.indexs = []
this.idMap = []
}
6
Finally
I feel that the implementation of this is still flawed. If you designed such a class, how would you design it?