Home>Article>Web Front-end> How to implement the barrage component in native JavaScript

How to implement the barrage component in native JavaScript

coldplay.xixi
coldplay.xixi forward
2020-10-09 16:32:29 2127browse

JavaScriptcolumn today introduces you to the method of implementing barrage components in native JavaScript.

How to implement the barrage component in native JavaScript

Preface

Nowadays, almost all video websites have barrage functions, so today we will encapsulate one ourselves using nativeJavaScriptBarrage type. This class hopes to have the following attributes and instance methods:

Attribute

  • elThe selector of the container node. The container node should be absolutely positioned and the width and height should be set.
  • heightThe height of each barrage
  • modeIn barrage mode, half is half the height of the container, and top is one-third. full is the time it takes for the barrage to cross the screen
  • speed
  • gapWidthThe distance between the next barrage and the previous barrage

Method

  • pushDataAdd barrage metadata
  • addDataContinue to join barrages
  • startStart scheduling barrage
  • stopStop barrage
  • restartRestart barrage
  • clearDataClear the barrage
  • closeClose
  • openRedisplay the barrage

PS: There are some self-encapsulated tool functions that I won’t post here. You just need to know the meaning.

Initialization

After introducing the JavaScript file, we hope to use it as follows , first adopt the default configuration.

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 see howgetBoxSizeandinitRows

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'))) } }复制代码

passgetComputedStyleapi calculates the width and height of the box, which is used to calculate the width and height of the container and will be used later.

function initRows(box, mode, height) { let pisor = getpisor(mode) rows = Math.ceil(box.height * pisor / height) return rows }function getpisor(mode) { let pisor = .5 switch (mode) { case 'half': pisor = .5 break case 'top': pisor = 1 / 3 break; case 'full': pisor = 1; break default: break; } return pisor }复制代码

Calculate how many lines the barrage should have based on the height. The number of lines will be used somewhere below.

Insert data

There are two ways to insert data, one is to add source data, and the other is to continuously add. Let’s first look at the method of adding source data:

this.pushData = function (data) { this.initDom() if (getType(data) == '[object Object]') { //插入单条 this.pushOne(data) } if (getType(data) == '[object Array]') { //插入多条 this.pushArr(data) } }this.initDom = function () { if (!document.querySelector(`${el} .barrage-list`)) { //注册dom节点 for (let i = 0; i < this.rows; i++) { let p = document.createElement('p') p.classList = `barrage-list barrage-list-${i}` p.style.height = `${this.boxSize.height*getpisor(this.mode)/this.rows}px` this.container.appendChild(p) } } }复制代码
this.pushOne = function (data) { for (let i = 0; i < this.rows; i++) { if (!this.list[i]) this.list[i] = [] } let leastRow = getLeastRow(this.list) //获取弹幕列表中最少的那一列,弹幕列表是一个二维数组 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 } }) }//根据行数把一维的弹幕list切分成rows行的二维数组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 is just to call the method of adding source data and start scheduling

this.addData = function (data) { this.pushData(data) this.start() }复制代码

Launch barrages

Let’s take a look at the logic of launching barrages

this.start = function () { //开始调度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 } //真正的调度从这里开始,用一个实例变量存储好当前调度的下标。 if (row[this.indexs[i]]) { this.dispatchItem(row[this.indexs[i]], i, this.indexs[i]) } }复制代码
this.dispatchItem = function (item, i) { //调度过一次的某条弹幕下一次在调度就不需要了 if (!item || this.idMap[item.id]) { return } let index = this.indexs[i] this.idMap[item.id] = item.id let p = document.createElement('p'), parent = document.querySelector(`${el} .barrage-list-${i}`), width, pastTime p.innerHTML = item.content p.className = 'barrage-item' parent.appendChild(p) width = getBoxSize(p).width p.style = `width:${width}px;display:none` pastTime = this.computeTime(width) //计算出下一条弹幕应该出现的时间 //弹幕飞一会~ this.run(p) if (index > this.list[i].length - 1) { return } let len = this.timeoutFuncs.length //记录好定时器,后面清空 this.timeoutFuncs[len] = setTimeout(() => { this.indexs[i] = index + 1 //递归调用下一条 this.dispatchItem(this.list[i][index + 1], i, index + 1) }, pastTime); }复制代码
//用css动画,整体还是比较流畅的this.run = function (item) { item.classList += ' running' item.style.left = "left:100%" item.style.display = '' item.style.animation = `run ${this.speed/1000}s linear` //已完成的打一个标记 setTimeout(() => { item.classList+=' done' }, this.speed); }复制代码
//根据弹幕的宽度和gapWth,算出下一条弹幕应该出现的时间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

@keyframes run { 0% { left: 100%; } 50% { left: 0 } 100% { left: -100%; } }.run { animation-name: run; }复制代码

Other methods

Stop

Use the paused attribute of the animation to stop

this.stop = function () { let items = document.querySelectorAll(`${el} .barrage-item`); [...items].forEach(item => { item.className += ' pause' }) }复制代码
.pause { animation-play-state: paused !important; }复制代码

Start again

Remove the pause class

this.restart = function () { let items = document.querySelectorAll(`${el} .barrage-item`); [...items].forEach(item => { removeClassName(item, 'pause') }) }复制代码

Open and close

Just make a show-hidden logic

this.close = function () { this.container.style.display = 'none'}this.open = function () { this.container.style.display = ''}复制代码

Clean up the barrages

this.clearData = function () { //清除list this.list = [] //清除dom document.querySelector(`${el}`).innerHTML = '' //清除timeout this.timeoutFuncs.forEach(fun => clearTimeout(fun)) }复制代码

Finally, use a timer to clean up the expired barrages:

setInterval(() => { let items = document.querySelectorAll(`${el} .done`); [...items].forEach(item=>{ item.parentNode.removeChild(item) }) }, this.speed*5);复制代码

Finally

I feel that the implementation of this is still flawed. If you designed it like this How would you design a class?

Related free learning recommendations:javascript(Video)

The above is the detailed content of How to implement the barrage component in native JavaScript. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:juejin.im. If there is any infringement, please contact admin@php.cn delete