fix(wish-tree): 弹幕一行一条排队播放,不再重叠
Publish Mini Program Dev Version / publish (push) Successful in 1m4s
Publish Mini Program Dev Version / publish (push) Successful in 1m4s
- 顶部滚动行上移紧贴置顶轮播(16%→12%) - 树桩3行改为68/73/78%,更紧凑 - 核心修复重叠:弃用CSS无限循环+负delay(同屏多条), 改为JS定时器按行调度,同一行一条飘完再出下一条,循环播放 - 弹幕动画改为只播一次,key含时间戳保证切换时重新触发动画 - 页面onHide/onUnload停止定时器,onShow恢复
This commit is contained in:
@@ -75,6 +75,21 @@ Page({
|
||||
this.loadProducts();
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
this.stopDanmakuScheduler();
|
||||
},
|
||||
|
||||
onHide() {
|
||||
this.stopDanmakuScheduler();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 从后台/下一页返回时,若已有弹幕数据则恢复调度
|
||||
if (this.danmakuRows && this.danmakuRows.length && !(this.danmakuTimers && this.danmakuTimers.length)) {
|
||||
this.startDanmakuScheduler(this.danmakuRows);
|
||||
}
|
||||
},
|
||||
|
||||
onPullDownRefresh() {
|
||||
const { currentTreeIndex, trees } = this.data;
|
||||
const tree = trees[currentTreeIndex];
|
||||
@@ -224,16 +239,75 @@ Page({
|
||||
this.setData({
|
||||
[`trees[${index}].hangWishes`]: cache.hangWishes,
|
||||
topWishes: cache.top,
|
||||
currentDanmaku: cache.danmaku,
|
||||
});
|
||||
// 启动弹幕调度:每行一条完毕后再出下一条
|
||||
this.startDanmakuScheduler(cache.danmaku);
|
||||
},
|
||||
|
||||
// 启动弹幕调度:每行同一时刻只播一条,前一条飘完再出下一条(循环)
|
||||
startDanmakuScheduler(rows) {
|
||||
this.stopDanmakuScheduler();
|
||||
this.danmakuRows = rows || [];
|
||||
this.danmakuTimers = [];
|
||||
|
||||
if (!this.danmakuRows.length) {
|
||||
this.setData({ currentDanmaku: [] });
|
||||
return;
|
||||
}
|
||||
|
||||
const GAP = 1.5; // 同一行前一条飘完后,间隔再出下一条(秒)
|
||||
|
||||
// 每行独立调度:row 行的第 idx 条播完后,隔 GAP 秒播第 idx+1 条
|
||||
const playRow = (row, idx) => {
|
||||
const rowData = this.danmakuRows[row];
|
||||
if (!rowData) return;
|
||||
const item = rowData.list[idx % rowData.list.length];
|
||||
// 更新该行当前弹幕(用行号作为 key 保证 setData 局部刷新)
|
||||
this.setData({
|
||||
[`currentDanmaku[${row}]`]: {
|
||||
key: `${row}-${idx % rowData.list.length}-${Date.now()}`,
|
||||
id: item.id,
|
||||
content: item.content,
|
||||
author: item.author,
|
||||
type: item.type,
|
||||
top: rowData.top,
|
||||
duration: rowData.duration,
|
||||
},
|
||||
});
|
||||
// 前一条飘完 + 间隔后,播下一条
|
||||
const timer = setTimeout(() => {
|
||||
playRow(row, idx + 1);
|
||||
}, (rowData.duration + GAP) * 1000);
|
||||
this.danmakuTimers[row] = timer;
|
||||
};
|
||||
|
||||
// 初始化 currentDanmaku 数组长度,并启动每行
|
||||
this.setData({ currentDanmaku: new Array(this.danmakuRows.length).fill(null) });
|
||||
this.danmakuRows.forEach((_, row) => {
|
||||
// 各行错开启动,避免 4 行同时从右侧涌出
|
||||
const startTimer = setTimeout(() => playRow(row, 0), row * 1200);
|
||||
this.danmakuTimers.push(startTimer);
|
||||
});
|
||||
},
|
||||
|
||||
// 停止弹幕调度
|
||||
stopDanmakuScheduler() {
|
||||
if (this.danmakuTimers) {
|
||||
this.danmakuTimers.forEach((t) => clearTimeout(t));
|
||||
this.danmakuTimers = [];
|
||||
}
|
||||
},
|
||||
|
||||
// 构建弹幕:共 4 行(行 0-2 树桩处三行,行 3 顶部置顶轮播下方)
|
||||
// 同行统一慢速,按条数均匀错开起始位置,避免互相盖住
|
||||
// 按行分组返回,由 JS 定时器控制:同一行一条完毕后再出下一条,不重叠
|
||||
buildDanmaku(list) {
|
||||
const DURATION_BOTTOM = 32; // 树桩处一行飘完全屏的秒数(慢速)
|
||||
const DURATION_TOP = 26; // 顶部行稍快一点
|
||||
const ROW_TOPS = [64, 71, 78, 16]; // 前三行靠近树桩,第四行在顶部
|
||||
const DURATION_BOTTOM = 30; // 树桩处一条飘完全屏的秒数(慢速)
|
||||
const DURATION_TOP = 24; // 顶部行稍快一点
|
||||
// 行位置(屏幕百分比):
|
||||
// - 行3 顶部行紧贴置顶轮播下方(稍有空隙)
|
||||
// - 行0-2 树桩处三行,第一条68%,三行紧凑(间隔5%)
|
||||
const ROW_TOPS = [68, 73, 78, 12];
|
||||
const ROW_DURATIONS = [DURATION_BOTTOM, DURATION_BOTTOM, DURATION_BOTTOM, DURATION_TOP];
|
||||
|
||||
// 轮流分配到 4 行
|
||||
const rows = [[], [], [], []];
|
||||
@@ -241,24 +315,17 @@ Page({
|
||||
rows[i % 4].push(w);
|
||||
});
|
||||
|
||||
const items = [];
|
||||
rows.forEach((rowList, row) => {
|
||||
if (rowList.length === 0) return;
|
||||
const duration = row === 3 ? DURATION_TOP : DURATION_BOTTOM;
|
||||
rowList.forEach((w, idx) => {
|
||||
items.push({
|
||||
id: w.id,
|
||||
content: w.content,
|
||||
author: w.author || '匿名',
|
||||
type: w.type,
|
||||
top: ROW_TOPS[row],
|
||||
duration,
|
||||
// 均匀间隔:同屏内前后错开,不会盖住其他弹幕
|
||||
delay: -(idx * duration / rowList.length),
|
||||
});
|
||||
});
|
||||
});
|
||||
return items;
|
||||
// 每行存该行的所有弹幕(带位置/时长),供定时器轮流播放
|
||||
return rows.map((rowList, row) => ({
|
||||
top: ROW_TOPS[row],
|
||||
duration: ROW_DURATIONS[row],
|
||||
list: rowList.map((w) => ({
|
||||
id: w.id,
|
||||
content: w.content,
|
||||
author: w.author || '匿名',
|
||||
type: w.type,
|
||||
})),
|
||||
})).filter((r) => r.list.length > 0);
|
||||
},
|
||||
|
||||
// 格式化许愿
|
||||
@@ -482,4 +549,10 @@ Page({
|
||||
path: '/pages/wish-tree/wish-tree',
|
||||
};
|
||||
},
|
||||
|
||||
onShareTimeline() {
|
||||
return {
|
||||
title: '快来许愿树许下你的愿望吧!'
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -45,14 +45,14 @@
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
|
||||
<!-- 弹幕层(超出50条的心愿缓缓飘过) -->
|
||||
<!-- 弹幕层(超出30条的心愿缓缓飘过,每行一条完毕再出下一条) -->
|
||||
<view class="danmaku-layer">
|
||||
<view
|
||||
wx:for="{{currentDanmaku}}"
|
||||
wx:key="id"
|
||||
class="danmaku-item {{item.type === 'paid' ? 'paid' : ''}}"
|
||||
style="top: {{item.top}}%; animation-duration: {{item.duration}}s; animation-delay: {{item.delay}}s;"
|
||||
>{{item.content}} · {{item.author}}</view>
|
||||
<block wx:for="{{currentDanmaku}}" wx:key="key" wx:if="{{item}}">
|
||||
<view
|
||||
class="danmaku-item {{item.type === 'paid' ? 'paid' : ''}}"
|
||||
style="top: {{item.top}}%; animation-duration: {{item.duration}}s;"
|
||||
>{{item.content}} · {{item.author}}</view>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<!-- 边缘暗角 -->
|
||||
|
||||
@@ -142,7 +142,8 @@
|
||||
text-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.6);
|
||||
animation-name: danmaku;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
animation-iteration-count: 1;
|
||||
animation-fill-mode: both;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
@@ -608,7 +609,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* 弹幕:从屏幕右侧飘到左侧 */
|
||||
/* 弹幕:从屏幕右侧飘到左侧。每条只播一次,由 JS 控制一行一条排队出现。 */
|
||||
@keyframes danmaku {
|
||||
0% {
|
||||
transform: translateX(100vw);
|
||||
|
||||
Reference in New Issue
Block a user