feat(timeline): 模式配色区分 + 舞台放大/时间轴可拖高度 + 滚动条美化 + 镜头跟玩家
- 两模式各自背景色调:海选审核=暖棕 / 演出配置=冷青绿(顶栏/背景/列表/边框/滚动条全区分) - 舞台自适应填充放大(画布提到960×470),时间轴默认200px固定高+中间分隔条可上下拖调高度 - 时间轴滚动条换成纤细主题化样式(替代默认丑滚动条),随模式变色 - 镜头修正:默认锁玩家(模拟真机45°俯视跟随相机),换说话人不再推近,仅显式镜头点才移焦
This commit is contained in:
@ -210,11 +210,10 @@
|
||||
function activeDialogue(M, tau) { return M.clips.find(c => c.kind === "dialogue" && tau >= c.start && tau < c.start + c.dur) || null; }
|
||||
function activeAnim(M, actor, tau) { return M.clips.find(c => c.kind === "anim" && c.actor === actor && tau >= c.start && tau < c.start + c.dur) || null; }
|
||||
function focusWorldAt(M, tau) {
|
||||
// 真机=45°俯视跟随玩家的相机:默认锁玩家,不会因换说话人而推近;仅显式镜头点(camera clip)才移焦。
|
||||
let fp = null;
|
||||
M.clips.filter(c => c.kind === "camera").forEach(c => { if (tau >= c.start) fp = c.focus; });
|
||||
if (fp) { const p = anchorXZ(M.anchors, fp); if (p) return p; }
|
||||
const dlg = activeDialogue(M, tau);
|
||||
if (dlg && dlg.actor) return actorPosAt(M, dlg.actor, tau);
|
||||
if (actorsIn(M).includes("P1")) return actorPosAt(M, "P1", tau);
|
||||
const b = M.bounds; return { x: (b.minX + b.maxX) / 2, z: (b.minZ + b.maxZ) / 2 };
|
||||
}
|
||||
@ -225,7 +224,7 @@
|
||||
const TEMPLATE =
|
||||
'<div class="tl-stagepanel">' +
|
||||
' <div class="tl-mapinfo"></div>' +
|
||||
' <div class="tl-stagewrap"><canvas class="tl-stage" width="780" height="380"></canvas><div class="tl-choices hidden"></div></div>' +
|
||||
' <div class="tl-stagewrap"><canvas class="tl-stage" width="960" height="470"></canvas><div class="tl-choices hidden"></div></div>' +
|
||||
' <div class="tl-controls">' +
|
||||
' <button class="tl-play primary">▶ 播放</button>' +
|
||||
' <button class="tl-restart mini">⏮ 重头</button>' +
|
||||
@ -235,6 +234,7 @@
|
||||
' <span class="tip">单击节点选中→「从选中处开始」,或双击节点直接开始(位置按途中走位重放) · 遇选择/战斗/随机弹选项</span>' +
|
||||
' </div>' +
|
||||
'</div>' +
|
||||
'<div class="tl-resizer" title="拖动调整时间轴高度"></div>' +
|
||||
'<div class="tl-timelinepanel"><div class="tl-tracks"></div></div>';
|
||||
|
||||
function show(host, IR, DICT, POINTSETS) {
|
||||
@ -255,6 +255,8 @@
|
||||
startbtn: host.querySelector(".tl-startbtn"),
|
||||
fitbtn: host.querySelector(".tl-fitbtn"),
|
||||
time: host.querySelector(".tl-time"),
|
||||
resizer: host.querySelector(".tl-resizer"),
|
||||
timelinepanel: host.querySelector(".tl-timelinepanel"),
|
||||
playhead: null,
|
||||
};
|
||||
els.mapinfo.textContent = "点位集:" + psName + (ps.mapId ? "(地图 " + ps.mapId + ")" : "") +
|
||||
@ -265,6 +267,19 @@
|
||||
els.restart.onclick = () => restart();
|
||||
els.startbtn.onclick = () => { if (selNode) startFrom(selNode, true); };
|
||||
els.fitbtn.onclick = () => { fitMode = !fitMode; els.fitbtn.classList.toggle("on", fitMode); refreshTimeline(); renderFrame(); };
|
||||
// 拖拽分隔条调时间轴高度(向上拖=时间轴变高/舞台变小,反之)
|
||||
els.resizer.onmousedown = e => {
|
||||
e.preventDefault();
|
||||
const startY = e.clientY, startH = els.timelinepanel.offsetHeight;
|
||||
document.body.style.cursor = "row-resize";
|
||||
const mv = ev => {
|
||||
const h = Math.max(80, Math.min((els.host.clientHeight - 150), startH - (ev.clientY - startY)));
|
||||
els.timelinepanel.style.height = h + "px";
|
||||
if (fitMode) { refreshTimeline(); renderFrame(); }
|
||||
};
|
||||
const up = () => { document.removeEventListener("mousemove", mv); document.removeEventListener("mouseup", up); document.body.style.cursor = ""; };
|
||||
document.addEventListener("mousemove", mv); document.addEventListener("mouseup", up);
|
||||
};
|
||||
|
||||
startFrom(firstNode(IR));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user