0
点赞
收藏
分享

微信扫一扫

js实现弹幕

点亮自己的那盏灯 2022-01-21 阅读 40

目前代码还是有一些bug,主要是调整浏览器大小的时候会出现文字运动停顿等问题,后续有时间在解决,不同文字长度速度不同而产生的文字堆叠问题已经解决,原理就是追击原理,代码如下,看效果可以直接拷贝到vscode打开浏览器运行即可

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <style>
      .container {
        width: 600px;
        height: 400px;
        border: 1px solid #ccc;
        margin: 0 auto;
        position: relative;
      }
      .scroll {
        white-space: nowrap;
        transition: all 3s;
      }
      .input {
        position: absolute;
        right: -220px;
        bottom: 0;
      }
      .anima {
        animation: fade 2s ease-in-out linear;
      }
    </style>
    <div class="container">
      <div class="input">
        <input type="text" id="text" />
        <button id="btn">发送</button>
      </div>
    </div>

    <script>
      let container = document.querySelector(".container");
      let text = document.getElementById("text");
      let {
        width: containerWidth,
        left: containerLeft,
        right: containerRight,
      } = container.getBoundingClientRect();
      let bullet = {
        left: 0,
        duration: 0,
        startTime: 0,
        top: 0,
        width: 0,
        height: 0,
      };
      let cnStr = "这还是一个非常狗血的故事";
      let abc = "abcdefghigklmnopqrstuvwsyg";
      let upperABC = abc.toUpperCase();
      let channel = [];
      function randomNum(max, min) {
        return parseInt(Math.random() * (max - min + 1) + min, 10);
      }
      function productData() {
        let arr = [];
        let preWidth = 600;
        for (let i = 0; i < 1000; i++) {
          let str =
            i +
            cnStr.substr(0, randomNum(-1, cnStr.length)) +
            upperABC.substr(0, randomNum(-1, upperABC.length)) +
            abc.substr(0, randomNum(-1, abc.length));
          arr.push(str);
        }
        return arr;
      }
      function initBullet(str, top, type = "") {
        if (str) {
          let div = document.createElement("div");
          div.innerHTML = str;
          div.style.position = "absolute";
          div.style.left = containerWidth + "px";
          div.style.top = top + "px";
          div.style.whiteSpace = "nowrap";
          if (type === "current") {
            div.style.border = "1px solid red";
            div.className = "anima";
          }
          container.appendChild(div);

          return div;
        }
        return null;
      }
      class Barrage {
        constructor(container, data, top) {
          this.data = data;
          this.channel = [];
          this.top = top;
          this.currentArr = [];
          this.init();
        }
        init() {
          this.loopWatcherLaunch = this.wrapper.call(this);
          this.loopWatcherLaunch.call(this);
        }
        wrapper() {
          let div = initBullet(this.data[0], this.top);
          let timer = null;
          return function (newDom = null) {
            if (!div) div = newDom;
            clearInterval(timer);
            let firstChnnel = this.channel[0];
            if (firstChnnel) {
              var firstChnnelRect = firstChnnel.getBoundingClientRect();
              if (
                parseInt(firstChnnelRect.right) - parseInt(containerLeft) <=
                0
              ) {
                container.removeChild(firstChnnel);
                this.channel.shift();
              }
            }
            // 子弹初始化
            let check = this.checkIsLauncch(div);
            if (check && div) {
              if (this.currentArr.length === 0) this.data.shift();
              launchBullet.call(this, div);
              if (this.currentArr.length > 0) {
                div = initBullet(this.currentArr.shift(), this.top, "current");
              } else {
                div = initBullet(this.data[0], this.top);
              }
            }
            if (this.channel.length > 0) {
              timer = setTimeout(() => {
                window.requestAnimationFrame(this.loopWatcherLaunch.bind(this));
              }, 0);
            }
          };
          function launchBullet(div) {
            let startTime = +new Date();
            div.startTime = startTime;
            div.duration = 5;
            let { width, right } = div.getBoundingClientRect();
            let leftDistance = right - containerLeft;
            div.moveV = (containerWidth + width) / div.duration;
            div.leftDruation = leftDistance / div.moveV;
            this.channel.push(div);
            div.style.transition = `all ${div.leftDruation}s linear 0s`;
            div.style.transform = `translateX(-${right - containerLeft}px)`;
          }
        }

        checkIsLauncch(dom) {
          let lastDom = this.channel[this.channel.length - 1];
          if (lastDom && dom) {
            let duration = 5;
            let lastRect = lastDom.getBoundingClientRect();
            let newsRect = dom.getBoundingClientRect();
            if (lastRect.right > containerRight - 20) return false;
            let lastS = lastRect.left - containerLeft + lastRect.width;
            let lastV = lastDom.moveV;
            let lastT = lastS / lastV;
            let newsS = containerWidth;
            let newsV = (containerWidth + newsRect.width) / duration;
            let newsT = newsS / newsV;
            if (newsT < lastT) {
              return false;
            }
          }
          return true;
        }
        addLaunch(value) {
          if (!value) {
            return;
          }
          if (this.data.length > 0) {
            this.currentArr.push(value);
          } else {
            this.data.unshift(value);
            this.loopWatcherLaunch.call(
              this,
              initBullet(this.data[0], this.top)
            );
          }
        }
        getCurrentDataLen() {
          return this.data.length;
        }
      }

      let barrageArr = [];
      let data = productData();
      // let barrage = new Barrage(container, ["222", "333"]);
      for (let i = 0; i < 5; i++) {
        let barrage = new Barrage(
          container,
          data.slice(i * 200, (i + 1) * 200),
          i * 80
        );
        barrageArr[i] = {
          instance: barrage,
          length: barrage.getCurrentDataLen(),
        };
      }
      btn.onclick = function () {
        let minItem = barrageArr[0].instance.getCurrentDataLen();
        let barrage = barrageArr[0].instance;
        barrageArr.forEach((item) => {
          if (minItem < item.instance.getCurrentDataLen()) {
            barrage = item.instance;
          }
        });
        barrage.addLaunch(text.value);
      };
    </script>
  </body>
</html>

举报

相关推荐

0 条评论