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>