业务逻辑
- 使用观察者模式来实现弹幕时,被观察者可以有n个,所以将每一个弹幕都作为一个被观察者,根据它不同的状态来进行不同的操作
- add,remove,update状态
- 观察keyCode为13时并且输入为非空时,我们进行add添加并且开启定时器不停的更新弹幕状态
- update时,我们改变弹幕在屏幕中的位置(根据我们的视频窗口来进行定位),观察弹幕宽度为0时我们执行删除定时器和并且删除清空box操作
代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.bullet{
width: 952px;
height: 40px;
font-size: 20px;
color: pink;
border: 2px solid #666666;
position: absolute;
top: 525px;
}
.window{
width: 960px;
height: 580px;
position: relative;
overflow: hidden;
margin: 50px auto;
}
</style>
</head>
<body>
//我们先写好html样式
<div class="window">
<video src="./video/周杰伦 - 不能说的秘密 (《不能说的秘密》电影插曲).mp4"></video>
<br>
<input class="bullet" type="text">
</div>
<!-- 每一条弹幕都是属于被观察者 -->
<script type="module">
import Bullet from "./observer/Bullet.js";
var input=document.querySelector(".bullet");
document.addEventListener("keyup",keyHandler); //侦听键盘事件
function keyHandler(e){
if(e.keyCode!==13||input.value.trim().length===0) return;
let bullet=new Bullet(input.value);
bullet.appendTo(".window");
input.value=""; //清空输入框
}
</script>
</body>
</html>
这里我们先写一个时间管理者,管理时间
export default class TimeManager{
static _instance;
list=new Set();
ids;
//只要被我们添加到list中就会执行update方法
//删除之后就会移除list后就会停止执行update方法
static get instance(){ //这里我们写一个单例模式
if (!TimeManager._instance) {
Object.defineProperty(TimeManager,"_instance",{
value:new TimeManager()
})
}
return TimeManager._instance;
}
constructor(){
}
add(elem){
this.list.add(elem);
if(this.list.size>0 && !this.ids)
this.ids=setInterval(()=>this.update(),16);
}
remove(elem){
this.list.delete(elem);
if(this.list.size===0 && this.ids){
clearInterval(this.ids) ;
this.ids=undefined;
}
}
update(){
this.list.forEach(item=>{
// console.log(item)
if(item.update) item.update();
})
}
}
这里开始写弹幕的实现逻辑
import TimeManager from "./TimeManager.js";
export default class Bullet{
rect;
x;
speed=2;
width;
elem;
constructor(txt){
this.elem=this.createElem(txt);
}
createElem(txt){ //创建弹幕
if(this.elem) return this.elem;
const box=document.createElement("div");
Object.assign(box.style,{
whiteSpace: "nowrap",
position:"absolute",
color:"#FF33FF"
})
box.textContent=txt;
return box;
}
appendTo(parent){ //将弹幕高度随机的放入视频中
if(typeof parent==="string") parent=document.querySelector(parent);
parent.appendChild(this.elem);
this.rect=parent.getBoundingClientRect(); //获取视频窗口的尺寸,为了确定我们弹幕出现的位置
Object.assign(this.elem.style,{
top:Math.random()*this.rect.height/2+"px",
left:this.rect.width+"px"
});
this.x=this.rect.width;
this.width=this.elem.offsetWidth;
TimeManager.instance.add(this);
}
update(){
if(!this.width) return; //当弹幕宽度不存在时return
this.x-=this.speed; //控制运动
this.elem.style.left=this.x+"px";
if(this.x<-this.width){
TimeManager.instance.remove(this); //当弹幕超出屏幕时移除弹幕
this.elem.remove(); //移除弹幕
this.elem=null;
}
}
}
至此我们的简单弹幕效果就实现了,当我们从后端接收到弹幕数据时,就进行数据插入渲染,基本所有的动画运动都可以使用我们的时间管理者,也就是观察者模式,也可以尝试使用AMD来写。