主体html结构
<div class="vedio-container">
<div class="barrage-container-wrap" ref="barWrapper">
<div class="video-wrapper">
<common-video @watchCurrent="watchCurrentTime"></common-video>
</div>
<div class="barrage-container" ref="bar"></div>
</div>
<div class="input-wrapper">
<input v-model="barrageInnerText" type="text">
<div @click="send">发送弹幕</div>
</div>
</div>
主要是:
<div class="barrage-container-wrap" ref="barWrapper">
<div class="video-wrapper">
<common-video @watchCurrent="watchCurrentTime"></common-video>
</div>
<div class="barrage-container" ref="bar"></div>
</div>
解释:
- barrage-container-wrap是最外层容器,包裹着video和弹幕层div
- video-wrapper是包裹着一个基础组件,commonVideo,监听一个watchCurrent的事件,这个事件主要用于,基础组件的video传送当前视频播放时间给父组件,父组件根据当前时间看是否有相应的弹幕,选择性发出
- barrage-container是弹幕层div,弹幕主要在这里显示
- 注意:由于弹幕层div的z-index必须要大于video才可以显示弹幕,所以commonVideo组件的video的controls组件要自己根据API设定,z-index大小:controls浮层>弹幕层>video,这样才可以既能控制,又能看弹幕,又能看视频
commonVideo组件html
<template>
<div class="common-video">
<video id="mycommonVideo" src="./../../assets/test.mp4"></video>
<div class="bottom-controls">
<span @click="changeStatus">{{playText}}</span>
<div class="progress-bar" ref="progressBar" @click="setBar">
<div class="progress-bar-active" ref="progressBarInner"></div>
</div>
<span class="time">{{currentT}} / {{allTime}}</span>
<span @click="addYinliang">音量+</span>
<span @click="decreaseYinliang">音量-</span>
<span @click="allScreen">全屏</span>
</div>
</div>
</template>
主要CSS
.vedio-container {
width: 80%;
height: 7rem;
margin: 0 auto;
font-size: 0.14rem;
margin-bottom: 1rem;
}
.barrage-container-wrap {
width: 100%;
height: 7rem;
position: relative;
overflow: hidden;
}
.barrage-container {
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 1;
bottom: 30px;
cursor: default;
user-select: none;
}
.barrage-item {
position: absolute;
top: 0;
left: 100%;
white-space: nowrap;
cursor: pointer;
color: #fff;
}
解释:
1rem = 100px(自己设定),barrage-item是弹幕item
主要数据段
data() {
return {
barrageArray: [ //假弹幕数据
{
time: '5',
text: '秋天爱美丽'
},
{
time: '3',
text: '2'
},
{
time: '11',
text: 'winter has come'
}
],
barrageColorArray: [ //弹幕颜色
'#0099CC', '#333333', '#009966', '#FFFF66', '#9933FF', '#FFFF99', '#CCCCFF', '#CC9933', '#FFFF66'
],
barrageInnerText: '', //弹幕内容
currentTime: 0, //弹幕时间
}
}
主要js代码
初始化dom
this.barrageBoxWrap = this.$refs.barWrapper
this.barrageBox = this.$refs.bar
this.barrageWidth = parseInt(this.barrageBoxWrap.offsetWidth)
this.barrageHeight = parseInt(this.barrageBoxWrap.offsetHeight)
父组件中:按下按钮发送input框中的内容 send方法
send() {
this.createBarrage(this.barrageInnerText, true) //制作弹幕
usersModel.sendBarrage({ //向后台发送post请求,发送当前视频时间,弹幕内容,存储到数据库中
time: this.currentTime,
content: this.barrageInnerText
}).then((res)=>{
console.log(res)
})
this.barrageInnerText = ''
}
createBarrage方法,参数1:弹幕内容,参数2:是否随机距离
createBarrage(msg, isSendMsg) {
//生产dom结点
let divNode = document.createElement('div')
divNode.innerHTML = msg
divNode.classList.add('barrage-item')
this.$refs.barWrapper.appendChild(divNode)
//设置偏离距离
var barrageOffsetLeft = this.getRandom(this.barrageWidth, this.barrageWidth * 2) //可设置随机距离
barrageOffsetLeft = isSendMsg ? this.barrageWidth : barrageOffsetLeft //不随机则就在距右边0px
var barrageOffsetTop = this.getRandom(10, this.barrageHeight - 40) //弹幕的高度
var barrageColor = this.barrageColorArray[Math.floor(Math.random() * (this.barrageColorArray.length))] //弹幕的颜色
//执行并初始化滚动
this.initBarrage(divNode, {
left: barrageOffsetLeft,
top: barrageOffsetTop,
color: barrageColor
})
}
getRandom方法
getRandom(start, end) {
return start + (Math.random() * (end - start))
}
initBarrage方法,初始化弹幕元素,参数1:弹幕dom,参数2:距离参数对象
initBarrage(el, obj) {
obj.top = obj.top || 0
obj.class = obj.color || '#fff'
el.style.left = obj.left + 'px'
el.style.top = obj.top + 'px'
el.style.color = obj.color
//添加属性
el.distance = 0
el.width = ~~window.getComputedStyle(el).width.replace('px','')
el.timer = null
this.barrageAnimation(el)
}
barrageAnimation方法,让弹幕动起来
barrageAnimation(el) {
let that = this
this.move(el)
if (Math.abs(el.distance) < el.width + el.offsetLeft) { //如果移动的距离小于弹幕的长度+外部div的宽度,则不断移动
el.timer = requestAnimationFrame(function () {
that.barrageAnimation(el);
})
} else {
cancelAnimationFrame(el.timer);
//删除节点
el.parentNode.removeChild(el);
}
}
解释:
cancelAnimationFrame,requestAnimationFrame是h5的请求动画帧的方法,递归实现平滑节能的动画效果
move方法
move(el) {
el.distance--;
el.style.transform = 'translateX('+el.distance+'px)'; //设置transform,平滑实现
el.style.webkitTransform = 'translateX('+el.distance+'px)';
}
这样基本可以实现发送弹幕的效果了
从后台接受数据,并在相应的时间进行发送
首先设计的数据库表很简单:
id字段,time字段,content字段
简单的sql语句和node.js代码不贴出来了,如有需要则私聊我,这里主要介绍思想
思想是:子组件的video,向父组件emit事件,每一秒就传递当前video的currentTime,父组件根据函数看时间是否吻合,如果吻合,则用send方法发送弹幕
子组件JS代码:
//每个一秒设置进度条并设置时间
setProgressBar() {
let outerBarWidth = this.$refs.progressBar.offsetWidth
this.oneTimer = setInterval(()=>{
this.setTimes(this.videoDom.currentTime,'current')
this.currentTimeNumber = this.videoDom.currentTime
let baifenbi = this.currentTimeNumber / this.allTimeNumber
let innerWidth = baifenbi * outerBarWidth
this.$refs.progressBarInner.style.width = innerWidth + 'px'
this.$emit('watchCurrent',Math.floor(this.currentTimeNumber)) //主要的代码是这一行,上面是设置conrtols的代码
},1000)
}
父组件代码:
<!-父组件监听-->
<common-video @watchCurrent="watchCurrentTime"></common-video>
barrageArray: [ //假弹幕数据,在created的时候连接后台,把数据覆盖上去
{
time: '5',
text: '秋天爱美丽'
}
]
//子组件传递的每一秒,看这一秒是否有弹幕
watchCurrentTime(seconds) {
this.currentTime = seconds
let barlen = this.barrageArray.length
let that = this
for(let i =0;i<barlen;i++){
let item = this.barrageArray[i]
if(!item){
continue
}
if(item.time == seconds){
that.createBarrage(item.text,true)
// that.barrageArray.splice(i,1)
}
}
}
//不知道这个算法写的好不好,有想过把已经显示的弹幕splice掉,但是这样重新点击到相应的时间就看不到弹幕了,如果有更好的方法也可以改为你自己的算法哦
至此大概的功能的实现了,自己做的video controls的功能没有贴出来
源代码:
请私聊我要哦
参考
https://blog.csdn.net/qq_32849999/article/details/81031234
里面更加详细,如有需要可以进去看看