前端要怎么做B站那樣的彈幕效果?非常有趣!?。?/h1>
前言
大家好,我是林三心,用最通俗易懂的話(huà)講最難的知識(shí)點(diǎn)是我的座右銘,基礎(chǔ)是進(jìn)階的前提是我的初心~
最近發(fā)現(xiàn)一個(gè)超級(jí)好玩的彈幕工具庫(kù),他就是:danmu
圖片
- 文檔: https://imtaotao.github.io/danmu/document/zh/
- github: https://github.com/imtaotao/danmu?tab=readme-ov-file
- 在線(xiàn)demo地址: https://imtaotao.github.io/danmu/
1、安裝依賴(lài)
npm install danmu
2、創(chuàng)建 manager
danmu
核心包只暴露了一個(gè) create
方法,用來(lái)創(chuàng)建一個(gè) manager
實(shí)例
import { create } from'danmu';
// 在此處創(chuàng)建一個(gè) manager 實(shí)例,如果不傳遞則會(huì)使用默認(rèn)的配置
const manager = create({
trackHeight: '20%',
plugin: {
$createNode(danmaku) {
danmaku.node.textContent = danmaku.data;
},
willRender(ref) {
console.log(ref.type); // 即將要渲染的彈幕類(lèi)型
console.log(ref.danmaku); // 即將要渲染的彈幕實(shí)例
ref.prevent = true; // 設(shè)置為 true 將阻止渲染,可以在這里做彈幕過(guò)濾工作
return ref;
},
},
// .
});
配置:
interface CreateOption {
rate?: number; // 用來(lái)設(shè)置設(shè)置彈幕的運(yùn)動(dòng)速率
interval?: number; // 內(nèi)核輪詢(xún)渲染的頻率
gap?: number | string; // 同一條軌道在碰撞檢測(cè)的啥情況下,后一條彈幕與前一條彈幕最小相隔的距離
durationRange?: [number, number]; // 普通彈幕的運(yùn)動(dòng)時(shí)間,這是一個(gè)范圍值,普通彈幕會(huì)在這個(gè)范圍內(nèi)隨機(jī)選擇一個(gè)時(shí)間作為運(yùn)動(dòng)時(shí)間,如果你希望所有的彈幕運(yùn)動(dòng)時(shí)間都一致,你可以將兩個(gè)數(shù)設(shè)置為同樣的數(shù)
trackHeight?: number | string; // 軌道高度
plugin?: ManagerPlugin;
limits?: { // 這個(gè)參數(shù)會(huì)限制彈幕渲染的數(shù)量
view?: number;
stash?: number;
};
direction?: 'right' | 'left'; // 彈幕的運(yùn)動(dòng)方向
mode?: 'none' | 'strict' | 'adaptive'; // 用來(lái)確定內(nèi)核的碰撞檢測(cè)算法
}
exportdeclarefunction create(options?: CreateOption): Manager;
3、掛載并渲染
當(dāng)我們創(chuàng)建好了一個(gè)manager
的時(shí)候就可以?huà)燧d到某個(gè)具體的節(jié)點(diǎn)上并渲染,實(shí)際上manager
內(nèi)部會(huì)啟動(dòng)一個(gè)定時(shí)器來(lái)輪詢(xún)將內(nèi)存區(qū)的彈幕來(lái)渲染出來(lái),而輪詢(xún)的時(shí)間是交由你來(lái)控制的
const container = document.getElementById('container');
// 掛載,然后開(kāi)始渲染
manager.mount(container);
manager.startPlaying();
4、發(fā)送普通彈幕
// 發(fā)送彈幕的內(nèi)容類(lèi)型可以在創(chuàng)建 manager 的時(shí)候通過(guò)范型來(lái)約定,可以看后面的章節(jié)
manager.push('彈幕內(nèi)容');
但是通過(guò)manager.push
方法來(lái)發(fā)送的彈幕可能會(huì)受到彈幕算法的影響,不會(huì)立即渲染,想象一些往一個(gè)數(shù)組里面 push 一個(gè)數(shù)據(jù),但是消費(fèi)是從數(shù)組最前端拿出數(shù)據(jù)消費(fèi)的。我們可以換一個(gè)manager.unshift
方法來(lái)發(fā)送彈幕
// 這會(huì)在下一次渲染輪詢(xún)中,立即渲染
manager.unshift('彈幕內(nèi)容');
5、發(fā)送高級(jí)彈幕
普通彈幕會(huì)受到碰撞渲染算法的限制,對(duì)于那些需要特殊處理的彈幕,例如頂部彈幕,特殊位置的彈幕,則需要通過(guò)manager.pushFlexibleDanmaku
這個(gè) API 發(fā)送高級(jí)彈幕來(lái)渲染,高級(jí)彈幕不會(huì)受到碰撞算法的限制
manager.pushFlexibleDanmaku('彈幕內(nèi)容', {
id: 1, // 可選參數(shù)
duration: 1000, // 默認(rèn)從 manager.options.durationRange 中隨機(jī)取一個(gè)值
direction: 'none', // 默認(rèn)取 manager.options.direction 的值
position: (danmaku, container) => {
// 這會(huì)讓彈幕在容器居中的位置出現(xiàn),因?yàn)?direction 為 none,所以會(huì)靜止播放 1s
return {
x: `50% - ${danmaku.getWidth() / 2}`,
y: `50% - ${danmaku.getHeight() / 2}`,
};
},
plugin: {
// plugin 參數(shù)是可選的,具體可以參考普通彈幕的鉤子,這里是一樣的
},
});