tweak(wish-tree): 弹幕各行速度错落,每行屏上2~3条不压着
Publish Mini Program Dev Version / publish (push) Successful in 46s
Publish Mini Program Dev Version / publish (push) Successful in 46s
- 顶部行回到12% - 每行不同基准速度:树桩三行26/33/40秒、顶部22秒,不再整齐划一 - 同行弹幕速度也带±20%随机错落(seeded保证渲染一致) - 每行屏上保持约2.5条:间隔=duration/2.5,但不小于完全进入时间 (duration*0.3,保证下一条不压着前一条)和最小间隔3秒
This commit is contained in:
@@ -245,7 +245,7 @@ Page({
|
|||||||
},
|
},
|
||||||
|
|
||||||
// 启动弹幕调度:每行一个发射定时器,前一条完全进入屏幕后就出下一条,
|
// 启动弹幕调度:每行一个发射定时器,前一条完全进入屏幕后就出下一条,
|
||||||
// 同一时刻屏上可有多条,但首尾相接不重叠(自然弹幕效果)
|
// 同一行屏上保持 2~3 条,首尾相接不压着;各行速度不同、同行弹幕速度也错落
|
||||||
startDanmakuScheduler(rows) {
|
startDanmakuScheduler(rows) {
|
||||||
this.stopDanmakuScheduler();
|
this.stopDanmakuScheduler();
|
||||||
this.danmakuRows = rows || [];
|
this.danmakuRows = rows || [];
|
||||||
@@ -257,15 +257,16 @@ Page({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 弹幕完全进入屏幕大约需要飘过总路程的 35%(从右屏外到尾部越过右边缘)
|
// 屏上目标条数:同一行同时约 2.5 条
|
||||||
// 此时下一条即可开始从右侧进入,与前一条首尾相接不重叠
|
const ON_SCREEN = 2.5;
|
||||||
const ENTER_RATIO = 0.35;
|
// 弹幕完全进入屏幕约需飘过总路程的 30%(不压着前一条的最小间隔)
|
||||||
const MIN_INTERVAL = 4; // 同一行相邻两条最小间隔(秒),避免过密
|
const ENTER_RATIO = 0.3;
|
||||||
|
const MIN_INTERVAL = 3; // 同一行相邻两条最小间隔(秒)
|
||||||
|
|
||||||
// 往某行发射一条弹幕
|
// 往某行发射一条弹幕,返回该条的 duration(供调度下一条用)
|
||||||
const emit = (row, idx) => {
|
const emit = (row, idx) => {
|
||||||
const rowData = this.danmakuRows[row];
|
const rowData = this.danmakuRows[row];
|
||||||
if (!rowData) return;
|
if (!rowData) return 0;
|
||||||
const item = rowData.list[idx % rowData.list.length];
|
const item = rowData.list[idx % rowData.list.length];
|
||||||
const seq = ++this.danmakuSeq;
|
const seq = ++this.danmakuSeq;
|
||||||
const danmaku = {
|
const danmaku = {
|
||||||
@@ -275,7 +276,7 @@ Page({
|
|||||||
author: item.author,
|
author: item.author,
|
||||||
type: item.type,
|
type: item.type,
|
||||||
top: rowData.top,
|
top: rowData.top,
|
||||||
duration: rowData.duration,
|
duration: item.duration,
|
||||||
};
|
};
|
||||||
// 追加到屏上弹幕列表(构造新数组,避免索引冲突)
|
// 追加到屏上弹幕列表(构造新数组,避免索引冲突)
|
||||||
this.setData({ currentDanmaku: this.data.currentDanmaku.concat(danmaku) });
|
this.setData({ currentDanmaku: this.data.currentDanmaku.concat(danmaku) });
|
||||||
@@ -283,17 +284,20 @@ Page({
|
|||||||
const removeTimer = setTimeout(() => {
|
const removeTimer = setTimeout(() => {
|
||||||
const list = this.data.currentDanmaku.filter((d) => d.key !== danmaku.key);
|
const list = this.data.currentDanmaku.filter((d) => d.key !== danmaku.key);
|
||||||
this.setData({ currentDanmaku: list });
|
this.setData({ currentDanmaku: list });
|
||||||
}, rowData.duration * 1000);
|
}, item.duration * 1000);
|
||||||
this.danmakuTimers.push(removeTimer);
|
this.danmakuTimers.push(removeTimer);
|
||||||
|
return item.duration;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 每行独立调度:每隔 interval 秒发一条,循环
|
// 每行独立调度:按刚发射这条的速度决定下一条间隔,循环
|
||||||
this.danmakuRows.forEach((rowData, row) => {
|
this.danmakuRows.forEach((rowData, row) => {
|
||||||
const interval = Math.max(rowData.duration * ENTER_RATIO, MIN_INTERVAL);
|
|
||||||
let idx = 0;
|
let idx = 0;
|
||||||
const loop = () => {
|
const loop = () => {
|
||||||
emit(row, idx);
|
const duration = emit(row, idx);
|
||||||
idx += 1;
|
idx += 1;
|
||||||
|
// 下一条间隔:屏上保持约 2.5 条(duration/2.5),
|
||||||
|
// 但不小于“完全进入”时间(duration*0.3,不压着)和最小间隔
|
||||||
|
const interval = Math.max(duration / ON_SCREEN, duration * ENTER_RATIO, MIN_INTERVAL);
|
||||||
const timer = setTimeout(loop, interval * 1000);
|
const timer = setTimeout(loop, interval * 1000);
|
||||||
this.danmakuTimers.push(timer);
|
this.danmakuTimers.push(timer);
|
||||||
};
|
};
|
||||||
@@ -316,15 +320,14 @@ Page({
|
|||||||
},
|
},
|
||||||
|
|
||||||
// 构建弹幕:共 4 行(行 0-2 树桩处三行,行 3 顶部置顶轮播下方)
|
// 构建弹幕:共 4 行(行 0-2 树桩处三行,行 3 顶部置顶轮播下方)
|
||||||
// 按行分组返回,由 JS 定时器控制:前一条完全进入屏幕后就出下一条,首尾相接不重叠
|
// 每行速度不同、同行弹幕速度也带随机错落,避免太整齐;前一条完全进入后出下一条
|
||||||
buildDanmaku(list) {
|
buildDanmaku(list) {
|
||||||
const DURATION_BOTTOM = 30; // 树桩处一条飘完全屏的秒数(慢速)
|
|
||||||
const DURATION_TOP = 24; // 顶部行稍快一点
|
|
||||||
// 行位置(屏幕百分比):
|
// 行位置(屏幕百分比):
|
||||||
// - 行3 顶部行紧贴置顶轮播下方(稍有空隙)
|
// - 行3 顶部行紧贴置顶轮播下方(稍有空隙)
|
||||||
// - 行0-2 树桩处三行,第一条70%,三行紧凑(间隔3%)
|
// - 行0-2 树桩处三行,第一条70%,三行紧凑(间隔3%)
|
||||||
const ROW_TOPS = [70, 73, 76, 14];
|
const ROW_TOPS = [70, 73, 76, 12];
|
||||||
const ROW_DURATIONS = [DURATION_BOTTOM, DURATION_BOTTOM, DURATION_BOTTOM, DURATION_TOP];
|
// 每行基准速度(秒/次):各行不同,避免整齐划一
|
||||||
|
const ROW_BASE_DURATION = [26, 33, 40, 22];
|
||||||
|
|
||||||
// 轮流分配到 4 行
|
// 轮流分配到 4 行
|
||||||
const rows = [[], [], [], []];
|
const rows = [[], [], [], []];
|
||||||
@@ -335,12 +338,13 @@ Page({
|
|||||||
// 每行存该行的所有弹幕(带位置/时长),供定时器轮流播放
|
// 每行存该行的所有弹幕(带位置/时长),供定时器轮流播放
|
||||||
return rows.map((rowList, row) => ({
|
return rows.map((rowList, row) => ({
|
||||||
top: ROW_TOPS[row],
|
top: ROW_TOPS[row],
|
||||||
duration: ROW_DURATIONS[row],
|
list: rowList.map((w, i) => ({
|
||||||
list: rowList.map((w) => ({
|
|
||||||
id: w.id,
|
id: w.id,
|
||||||
content: w.content,
|
content: w.content,
|
||||||
author: w.author || '匿名',
|
author: w.author || '匿名',
|
||||||
type: w.type,
|
type: w.type,
|
||||||
|
// 同行弹幕速度也带 ±20% 错落(seeded 保证每次渲染一致)
|
||||||
|
duration: Math.round(ROW_BASE_DURATION[row] * (0.8 + seeded(w.id != null ? w.id : i, row + 7) * 0.4)),
|
||||||
})),
|
})),
|
||||||
})).filter((r) => r.list.length > 0);
|
})).filter((r) => r.list.length > 0);
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user