UNPKG

@silkytone/danmu

Version:

弹幕的简单实现,实现普通弹幕或高级弹幕。

112 lines (102 loc) 3.49 kB
// core/barrage.ts import { BarrageItem, BarrageOptions } from '../interface'; import { Animation, Tracks } from '../lib'; import { mergeObject } from '../utils'; // TODO: 弹幕管理 export class Barrage { private readonly tracks: Tracks; private readonly $el: HTMLElement; private readonly animation: Animation; private readonly opt: Record<string | number, any>; private readonly size: { width: number; height: number }; // TODO: 移动区 private moveTracks: { start: number; step: number; value: BarrageItem }[] = []; private waitTracks: BarrageItem[] = []; private status: boolean = false; constructor(element: HTMLElement | string, options?: Partial<BarrageOptions>) { this.opt = mergeObject({ limit: 60 }, options || {}); this.$el = this.createElement(element); this.animation = new Animation(this.opt.limit); this.size = { width: this.$el.clientWidth, height: this.$el.clientHeight, }; this.tracks = new Tracks({ ...this.opt, ...this.size }); } createElement(element: HTMLElement | string) { const el = (() => { if (typeof element === 'string') { const el = document.querySelector<HTMLElement>(element); if (!el) throw new Error('element not found'); return el; } else { return element; } })(); // el.innerHTML = ''; el.style.width = '100%'; el.style.height = '100%'; el.style.overflow = 'hidden'; el.style.userSelect = 'none'; el.style.position = 'relative'; el.style.pointerEvents = 'none'; // return el; } playAnimation() { if (this.status) return; this.status = true; const minWidth = this.size.width; this.animation.run(() => { const now = Date.now(); for (const [index, item] of Object.entries(this.moveTracks)) { const value = (now - item.start) * item.step; const maxWidth = minWidth + item.value.width; if (value > maxWidth) { item.value.destroy(); this.moveTracks.splice(parseInt(index), 1); } else { item.value.$el.style.transform = `translateX(-${value}px)`; } } this.addWaitTracks(); if (!this.moveTracks.length) { this.animation.stop(); this.status = false; } }); } private addWaitTracks() { if (!this.waitTracks.length) return false; this.waitTracks = this.waitTracks.filter(item => { const time = Date.now(); const { $el: el, width, height, duration } = item; const track = this.tracks.add(width, height, duration, time); if (track) { el.style.top = this.tracks.trackToPx(track.index) + 'px'; const step = Math.ceil(this.size.width / duration * 1000000) / 1000000; this.moveTracks.push({ start: time, step, value: item }); this.playAnimation(); return null; } return item; }); } push(item: BarrageItem): void { // TODO: 将弹幕挂载到等待区 this.$el.appendChild(item.$el); const time = Date.now(); const { $el: el, width, height, duration } = item; // const track = this.tracks.add(width, height, duration, time); if (track) { el.style.top = this.tracks.trackToPx(track.index) + 'px'; const step = Math.ceil(this.size.width / duration * 1000000) / 1000000; this.moveTracks.push({ start: time, step, value: item }); this.playAnimation(); } else { this.waitTracks.push(item); } } }