如果生活有奇迹,那一定是努力的轨迹!
复制代码
Hello,大家好,我是小羽同学
,一个平凡而又不甘于平凡的前端开发工程师~
嘿嘿嘿,好兄弟们,是不是很意外,该系列文章突然又更新
了!!!!
其实吧有两个原因。
一是阿宽连小册都出来了,而我的第一篇系列文
差点太监了,使得我备受打击。
二则是看到小伙伴们的学习热情
,没想到我断更了大半年,还是有不少小伙伴找上来和小羽聊聊天,小羽还是挺开心的 。虽然小羽目前是转了react
技术栈,但是为了不辜负小伙伴们对该系列文章的‘期盼’
吧,就先把主流程
完善一下吧。
本期的话主要就是和小伙伴们聊聊如何实现弹幕轨道
。
1.什么是弹幕轨道?
在上一期中,咱们已经简单的实现了弹幕的功能,但是存在这么一个问题,就是所有的弹幕都在同一条
水平线上播放的。当弹幕的数量稍微多一点点,就看不清
弹幕中的内容。那咱们有没有方法实现类似于直播平台
的那种可以全屏幕
的弹幕播放,而又尽量不会影响
到其他的弹幕呢?
答案是有的,小羽
称这种方式为弹幕轨道
。
如下图所示,就是将咱们直播视屏的可视区域
,动态划分
为几个轨道。然后咱们将咱们的弹幕随机推送
到这些弹幕轨道
中,然后弹幕按顺序从右侧移动到左侧,即可完成咱们的弹幕功能。
2.新增弹幕轨道组件
src/views/live中新增barrageStream.vue
<!--
* @Description:
* @Author: 小羽
* @LastEditors: 小羽
* @Date: 2021-06-07 21:53:30
* @LastEditTime: 2021-06-08 23:53:25
-->
<template>
<div class="barrage-stream" ref="barrageStream">
</div>
</template>
<script>
import { mapState, mapMutations } from "vuex";
export default {
name: "barrageStream",
data() {
return {
barrageStreamRect: {},
barrageStreamList: [],// 弹幕轨道
barrageStreamListNum: null,// 弹幕轨道数量
};
},
computed: {
...mapState({
barrageMsgList: (state) => state.barrage.barrageMsgList,
}),
},
methods: {
},
watch: {
},
mounted() {
},
};
</script>
<style lang="less" scoped>
.barrage-stream {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 9999;
.barrage-block {
z-index: 1;
position: relative;
height: 40px;
//border-bottom: #fff 1px solid;
width: 100%;
color: #fff;
&-item {
position: absolute;
display: inline-block;
width: 200px;
animation: barrage 5s linear;
animation-fill-mode: forwards;
}
}
}
@keyframes barrage {
from {
left: 100%;
transform: translateX(0);
}
to {
left: 0;
transform: translateX(-200%);
}
}
</style>
复制代码
3.动态计算弹幕轨道的数量
为啥要动态计算弹幕轨道的数量呢?我直接写死轨道的数量,简单快捷,它不香吗???
好兄弟们,想一下,咱们现在屏幕的种类
是不是很多,然后分辨率
的大小也不一致吧?不同分辨率
显示的弹幕轨道数量总该是不一样的吧?此外,咱们的用户也不一定是全屏
来观看直播的。说不定就喜欢开个小窗口
来观看呀。为了解决这类问题,咱们就需要动态的计算
弹幕轨道的数量了。
在计算弹幕轨道前,咱们先了解一个api——getBoundingClientRect()
该方法会返回元素的大小以及其相对于视窗的位置。
在标准盒子模型
(box-sizing: content-box),元素的尺寸等于width/height
+ padding
+ border-width
的总和。在弹性盒子模型
(box-sizing: border-box),元素的的尺寸等于 width/height
。
简单的通过ref/id获取咱们的直播视屏区域的DOM结构
,然后调用getBoundingClientRect()
方法,在控制台中输出。可以看到会得到DOM结构的宽高
以及在视窗中的位置信息
。
再结合弹幕的高度,以及底部播放条的显示区域高度,咱们不难得到这么一个方法
methods: {
// 获取弹幕轨道播放流的数量
getBarrageStreamList() {
console.log(this.$refs.barrageStream.getBoundingClientRect());
// 获取dom元素的信息
this.barrageStreamRect = this.$refs.barrageStream.getBoundingClientRect();
// 计算轨道的数量
this.barrageStreamListNum = Math.floor(
(this.barrageStreamRect.height - 100) / 36
);
// 初始化轨道
this.barrageStreamList = [];
for (let i = 0; i < this.barrageStreamListNum; i++) {
this.barrageStreamList.push([]);
}
console.log(this.barrageStreamList);
},
},
复制代码
好了,现在方法有了,那咱们该什么时候调用该方法呢?
初始化的时候该调用到吧?那就加上。
mounted() {
this.getBarrageStreamList();
},
复制代码
但是现在问题来了,有些用户就喜欢捣蛋
,就喜欢随意缩放
浏览器的大小,这样一来咱们初始化时候的轨道数量就不对了,这时候怎么搞?
其实很简单,咱们只需要监听窗口
的大小变化就好啦。
那监听窗口的变化需要用到什么方法?
window.addEventListener("resize",function())
那既然都添加了监听,那咱们离开该组件的时候是不是也该把这个监听给移除掉?
移除用什么方法?
window.removeEventListener("resize",function())
ok,那咱们很容易就可以得到以下这段代码
mounted() {
this.getBarrageStreamList();
window.addEventListener("resize", this.getBarrageStreamList);
},
beforeDestroy() {
window.removeEventListener("resize", this.getBarrageStreamList);
},
复制代码
4.随机分配弹幕到弹幕轨道中
问:什么时候需要分配弹幕到轨道中?
答:在弹幕新增的时候。
问:怎么知道弹幕中数据发生变化?
答:监听vuex中的弹幕数组
所以我们对弹幕的分配
操作应该就是在监听中实现的。
插入一个vue面试题:
vue中怎么监听一个数组的变化?以及怎么实现初始化监听?
答案很简单,js 中会有基本数据类型和引用数据类型,而watch默认是监听基本数据类型的变化,而对引用数据类型只会监听其对应的存储地址,既不会对数组里面的数据进行监听。当咱们想对数组里面的数据进行监听时,添加
deep:true
即可实现深度监听。而初始化监听则是添加另外一个参数immediate:true
即可
然后我们是怎么获取随机轨道
呢?
可以结合Math.randon()
方法,然后再将弹幕push
到咱们的随机轨道中。
为了节省
后续渲染上的开销
,我们显示过的弹幕是不是不该继续存在于我们的DOM中?
所以结合setTimeout
将那些渲染过的弹幕移除。
最后得到以下代码
watch: {
barrageMsgList: {
handler(newval) {
if (newval.length) {
// 获取随机轨道下标
let randomNum = Math.floor(Math.random() * this.barrageStreamListNum);
// 将弹幕推送到随机轨道中
this.barrageStreamList[randomNum].push(newval[newval.length - 1]);
// 延时5s后,删除该弹幕。(时间与弹幕的播放时间有关)
setTimeout(() => {
this.barrageStreamList[randomNum].shift();
}, 5000);
}
},
deep: true,
},
},
复制代码
5.渲染弹幕
修改template中的代码,保存。
<template>
<div class="barrage-stream" ref="barrageStream">
<div
class="barrage-block"
v-for="(stream, streamIndex) of barrageStreamList"
:key="'barrageStreamList' + streamIndex"
>
<div
class="barrage-block-item"
v-for="(item, index) of stream"
:key="'barrage-block-item' + index"
>
{{ item.msg }}
</div>
</div>
</div>
</template>
复制代码
打开页面,输入弹幕,弹幕轨道出来了
但是小伙伴们,仔细观察后悔发现有一些弹幕还没播放完就消失了,这是怎么回事呀?
小羽在这里将结合一道vue的面试题进行解答
v-for中为什么不建议使用index做key值?
主要是因为数组操作会导致显示异常。
让我们来结合我们的弹幕来进行讲解把。
如下图所示,主要是因为key值是绑定了index值,而某些css样式却是通过index进行绑定的。当咱们删除数组中的某个值时,key值和index值重新绑定了我们的弹幕内容,样式也随之变化,从而引起这种显示异常。
要解决这个问题其实也很简单,就是不用index作为key值即可。
<template>
<div class="barrage-stream" ref="barrageStream">
<div
class="barrage-block"
v-for="(stream, streamIndex) of barrageStreamList"
:key="'barrageStreamList' + streamIndex"
>
<div class="barrage-block-item" v-for="item of stream" :key="item.id">
{{ item.msg }}
</div>
</div>
</div>
</template>
复制代码
修改发送弹幕的方法(添加一个随机数作为id即可),路径src/views/live/barrage.vue
//发送弹幕
chatLiveRoom() {
console.log(this.currentUser);
if (!this.currentUser.id) {
this.$Message.error("请登录后再发言~");
} else if (this.battageMsg) {
let data = {
room: this.livingRoom,
func: "chatLiveRoom",
data: {
id: common.getCode(8),
user: this.currentUser.name,
msg: this.battageMsg,
},
};
this.$sockBarrage.roomChat(data);
this.battageMsg = "";
} else {
this.$Message.error("请输入弹幕内容!");
}
},
复制代码
小结
距离小羽上次写本系列文已经是半年以前
的事情了。
真的挺久了,久到小羽都忘了
代码的内容了。
外加来到了新公司,工作还是比较忙的。
但还是抽空看了下代码和前几期的内容,终于
把这期的文章啃了出来。
想想还是挺唏嘘
的,哈哈哈。
如果看这系列文章后,感觉有收获的小伙伴们可以点赞
+关注
哦~
如果想和小羽
交流技术可以加下wx,也欢迎小伙伴们来和小羽唠唠嗑
,嘿嘿~
ps:纯原创,转载请标明出处
最后奉上github链接,可以的话帮忙点个小星星,谢谢老铁~