本彈幕元件要實作的效果:
在輸入框中按下Enter鍵以後将輸入框中的内容生成為彈幕,彈幕從視訊區域的右側進入,不斷向左移動,當彈幕完全移動到視訊區域的左側外面時使彈幕從記憶體中消失,防止記憶體洩漏。
生成彈幕的條件:
按下的必須是Enter鍵,并且輸入框中的内容不能 全為空格或者沒有内容。
html代碼部分:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>彈幕</title>
<style>
/* 根據視訊寬高設定輸入框的寬度以及大容器的寬高 */
input {
width: 528px;
height: 40px;
font-size: 30px;
}
.div0 {
width: 536px;
height: 540px;
position: relative;
overflow: hidden;
}
</style>
</head>
<body>
<div class="div0">
<video src="./test.mp4" controls></video>
<input type="text" />
</div>
<script type="module">
import Bullet from "./Bullet.js";
var input = document.querySelector("input");
document.addEventListener("keyup", keyHandler);
function keyHandler(e) {
if (e.keyCode !== 13) return;
if (input.value.trim().length === 0) return;
var elem = new Bullet(input.value);
elem.appendTo(".div0");
input.value = "";
}
</script>
</body>
</html>
js代碼部分:
觀察者 TimeManager.js:
export default class TimeManager {
ids;
list = new Set();
static _instance;
constructor() {}
static get instance() {
if (!TimeManager._instance)
Object.defineProperty(TimeManager, "_instance", {
// 将TimeManager的_instance屬性的值設定為TimeM一個執行個體化對象anager的,并且該屬性是不可修改、不可枚舉、不可修改或删除描述對象的
value: new TimeManager(),
});
return TimeManager._instance;
}
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) => {
if (item.update) item.update();
});
}
}
被觀察者 Bullet.js:
import TimeManager from "./TimeManager.js";
export default class Bullet {
elem;
rect;
width;
x = 0;
speed = 2;
constructor(txt) {
this.elem = this.createElem(txt);
}
// 根據傳入的内容生成彈幕的内容
createElem(txt) {
if (this.elem) return this.elem;
let div = document.createElement("div");
Object.assign(div.style, {
// 防止彈幕剛進入視訊顯示區域時出現換行的情況
whiteSpace: "nowrap",
position: "absolute",
});
div.textContent = txt;
return div;
}
appendTo(parent) {
if (typeof parent === "string") parent = document.querySelector(parent);
parent.appendChild(this.elem);
// 擷取父容器的寬高及位置等屬性
this.rect = parent.getBoundingClientRect();
Object.assign(this.elem.style, {
// 限制彈幕到容器頂部的距離為父容器高度的1/4範圍内
top: (Math.random() * this.rect.height) / 4 + "px",
left: this.rect.width + "px",
});
this.x = this.rect.width;
this.width = this.elem.offsetWidth;
// 将目前執行個體化的彈幕對象添加到TimeManager.instance所對應的list集合中
TimeManager.instance.add(this);
}
update() {
if (!this.width) return;
this.x -= this.speed;
this.elem.style.left = this.x + "px";
// 如果目前執行個體彈幕對象已經完全移出了視訊區域,則将其從TimeManager.instance的list集合中删除,并且将其對應的DOM元素從記憶體中完全删除
if (this.x < -this.width) {
TimeManager.instance.remove(this);
this.elem.remove();
this.elem = null;
}
}
}