오늘
JavaScript 칼럼에서는 기본 JavaScript를 사용하여 Barrage 구성 요소를 구현하는 방법을 소개합니다.
요즘에는 거의 모든 비디오 웹사이트에 사격 기능이 있으므로 오늘은 네이티브 JavaScript
를 사용하여 사격 클래스를 캡슐화하겠습니다. 이 클래스는 다음과 같은 속성과 인스턴스 메소드를 갖고 싶어합니다: JavaScript
封装一个弹幕类。这个类希望有如下属性和实例方法:
el
容器节点的选择器,容器节点应为绝对定位,设置好宽高height
每条弹幕的高度mode
弹幕模式,half则为一半容器高度,top为三分之一,full为占满speed
弹幕划过屏幕的时间gapWidth
后一条弹幕与前一条弹幕的距离pushData
添加弹幕元数据addData
持续加入弹幕start
开始调度弹幕stop
停止弹幕restart
重新开始弹幕clearData
清空弹幕close
关闭open
重新显示弹幕PS:有一些自封装的工具函数就不贴出来了,大概知道意思就好
引入JavaScript文件之后,我们希望如下使用,先采取默认配置。
let barrage = new Barrage({ el: '#container'})复制代码
参数初始化:
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 = [] }复制代码
先接受好参数然后初始化,下面看看getBoxSize
和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'))) } }复制代码
通过getComputedStyle
el
컨테이너 노드에 대한 선택기, 컨테이너 노드는 다음과 같아야 합니다. 절대 위치 및 너비와 높이 설정height
각 포격의 높이mode
포격 모드에서는 절반이 절반입니다 컨테이너 높이, 상단은 1/3, 가득 참속도
탄격이 화면을 가로지르는 데 걸리는 시간 gapWidth
다음 글머리 기호와 이전 글머리 기호 사이의 거리pushData
글머리 기호 화면 메타데이터 추가addData
연속 사격 추가start
공격 예약 시작restart
포격 다시 시작clearData
포격 취소닫기
닫기열기
포격 다시 표시자바스크립트 파일을 소개한 후 아래와 같이 사용하려고 합니다. 먼저 기본값을 채택합니다. 구성.
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 }复制代码
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) } } }复制代码
매개변수를 먼저 승인한 후 초기화하세요. getCompulatedStyle을 통해 계산된 <code>getBoxSize
및 initRows
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 }复制代码
를 살펴보겠습니다. api 상자의 너비와 높이는 나중에 사용되는 컨테이너의 너비와 높이를 계산하는 데 사용됩니다.
this.addData = function (data) { this.pushData(data) this.start() }复制代码
데이터를 삽입하는 방법에는 원본 데이터를 추가하는 방법과 연속적으로 추가하는 방법이 있습니다. 먼저 소스 데이터를 추가하는 방법을 살펴보겠습니다.
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 }复制代码
@keyframes run { 0% { left: 100%; } 50% { left: 0 } 100% { left: -100%; } }.run { animation-name: run; }复制代码
this.stop = function () { let items = document.querySelectorAll(`${el} .barrage-item`); [...items].forEach(item => { item.className += ' pause' }) }复制代码
.pause { animation-play-state: paused !important; }复制代码
애니메이션 CSS는 다음과 같습니다
this.restart = function () { let items = document.querySelectorAll(`${el} .barrage-item`); [...items].forEach(item => { removeClassName(item, 'pause') }) }复制代码
Stop
this.close = function () { this.container.style.display = 'none'}this.open = function () { this.container.style.display = ''}复制代码
this.clearData = function () { //清除list this.list = [] //清除dom document.querySelector(`${el}`).innerHTML = '' //清除timeout this.timeoutFuncs.forEach(fun => clearTimeout(fun)) }复制代码
다시 시작
pause 클래스만 삭제하세요🎜이 구현이 여전히 결함이 있다고 생각합니다. 이런 수업, 어떻게 디자인하시겠습니까? 🎜🎜🎜🎜관련 무료 학습 권장사항: 🎜🎜🎜javascript🎜🎜🎜(동영상)🎜🎜🎜setInterval(() => { let items = document.querySelectorAll(`${el} .done`); [...items].forEach(item=>{ item.parentNode.removeChild(item) }) }, this.speed*5);复制代码열고 닫으세요 보이지 않는 논리를 만드세요 rrreee막대를 정리하세요rrreee마지막으로 타이머를 사용하여 만료된 막사를 정기적으로 정리하세요.rrreee마지막으로
위 내용은 기본 JavaScript에서 사격 구성 요소를 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!