jiangwei小站
745 字
4 分钟
近乎完美的横向弹幕

前言:#

横向弹幕在工作中并不少见,每次写都挺头疼的,比如动画卡顿,弹幕长短不一导致重叠,速度不一致等问题,于是准备这次好好写一个,以后可以直接用,不需要每次重新开发,特此记录。

用到的技术:#

vue、对象池、gsap、

对象池是什么?为什么要使用对象池?#

对象池顾名思义就是存放对象的池,当池子里没有对象的时候我们就创建对象,对象使用完成后将对象放回池子,下次需要用的时候重新拿出来用,这样就重复使用池子中的这些对象,不用一直创建,提高性能。

gsap 是什么?#

gsap 官网 gsap 是个很受欢迎的动画库。动画有一些回调监听,比如动画开始时,动画完成时,动画进行时,还有时间线,贝塞尔曲线,有众多插件,目前满足我的所有动画需求。

对象池:#

export default class NodePool {
  private nodeArr: HTMLElement[] = [];
  public get() {
    if (this.nodeArr.length > 0) {
      return this.nodeArr.shift();
    }
  }
  public put(dom: HTMLElement) {
    this.nodeArr.push(dom);
  }
  public size() {
    return this.nodeArr.length;
  }
  public clear() {
    this.nodeArr = [];
  }
}

弹幕组件#

<!-- @format -->
<template>
  <div class="rowBarrage"></div>
</template>

<script lang="ts">
import { Vue } from "vue-class-component";
import gsap from "gsap";
import CSSPlugin from "gsap/CSSPlugin";
import NodePool from "../../utils/NodePool";
class Props {}
export default class Demo extends Vue.with(Props) {
  // 对象池
  nodePool: NodePool = new NodePool();
  // 需要进行轮播的数据
  list = [
    "用户王***锤 抽到了一个男生纸条",
    "用户王***锤***锤 抽到了一个男生纸条",
    "用户王***锤***锤 抽到了一个男生纸条",
    "用户王***锤 抽到了一个男生纸条",
    "用户王***锤 抽到了一个男生纸条",
    "用户王***锤 抽到了一个男生纸条",
    "用户王***锤 抽到了一个男生纸条"
  ];
  num = -1;
  created() {
    gsap.registerPlugin(CSSPlugin);
  }
  mounted() {
    this.ani("0");
	// 多行的时候只需要增加top位置即可
    setTimeout(() => {
      this.ani("0.7rem");
    }, 1000);
  }
  ani(top: string) {
    this.num++;
    if (this.num > this.list.length - 1) {
      this.num = 0;
    }
	// 拿到弹幕的内容
    const msg: string = (this as any).list[this.num];
    let dom: HTMLElement;
    if (this.nodePool.size()) {
      // 从对象池中拿dom
      dom = this.nodePool.get()!;
    } else {
      // 创建dom
      dom = document.createElement("div");
      document.getElementsByClassName("rowBarrage")[0].appendChild(dom);
    }
    // 设置类名
    dom.setAttribute("class", "item");
    // 添加内容
    dom.innerHTML = `<span>${msg}</span>`;
    // 设置位置为屏幕右边
    gsap.set(dom, { x: "100vw", y: top });
	 let flag = true;
	 // 控制弹幕间距
	 const distance = 24;
	 // 滑动离开屏幕
    gsap.to(dom, {
      duration: 12, //控制动画速度
      x: "-100vw", //如果弹幕过长可以-200vw
      ease: "none",
      onComplete: () => {
        //动画结束,回收至对象池
        this.nodePool.put(dom);
      },
      onUpdate: () => {
        // 判断弹幕的右边边框离开右边可视窗口的时机
        if (
          dom.getBoundingClientRect().right < document.body.clientWidth - distance &&
          flag
        ) {
          flag = false;
          this.ani(top);
        }
      }
    });
  }
}
</script>
<style lang="less" scoped>
.rowBarrage {
  width: 7.5rem;
  height: 1.2rem;
  background-color: antiquewhite;
  position: relative;
  :deep(.item) {
    border-radius: 99px;
    background: rgba(0, 0, 0, 0.13);
    font-size: 0.24rem;
    color: rgba(34, 34, 34, 1);
    padding: 0 0.2rem;
    height: 0.48rem;
    line-height: 0.48rem;
    display: inline-flex;
    justify-content: center;
    align-items: center;
    position: absolute;
    top: 0;
    left: 0;
  }
}
</style>