天天看點

使用設計模式之觀察者模式來實作簡單的發送彈幕元件

本彈幕元件要實作的效果:

在輸入框中按下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;
    }
  }
}

           

繼續閱讀