diff --git a/web/static/style.css b/web/static/style.css index b7a3338..5c1755a 100644 --- a/web/static/style.css +++ b/web/static/style.css @@ -265,7 +265,7 @@ header .who { margin-left:auto; font-size:12px; color:#9a8f7e; } .tl-choice-btn:hover { background:#5a4a26; border-color:#e6c878; } /* 时间轴面板:独立、占满剩余高度、自己横向滚动 */ .tl-timelinepanel { flex:1; min-height:120px; margin-top:8px; display:flex; } -.tl-tracks { position:relative; flex:1; overflow-x:auto; overflow-y:auto; +.tl-tracks { position:relative; flex:1; min-width:0; overflow-x:auto; overflow-y:auto; cursor:grab; background:#19150f; border:1px solid #3a322a; border-radius:6px; padding-top:20px; } .tl-ruler { position:relative; height:16px; border-bottom:1px solid #2a2419; } .tl-tick { position:absolute; top:0; height:16px; border-left:1px solid #2a2419; } diff --git a/web/static/timeline.js b/web/static/timeline.js index 98769e8..8cb1da4 100644 --- a/web/static/timeline.js +++ b/web/static/timeline.js @@ -315,6 +315,12 @@ PX = fitMode ? Math.max(6, ((host.clientWidth || 760) - 60) / Math.max(model.total, 0.1)) : PXMAX; // 鼠标滚轮 → 横向滚动(横向溢出时) host.onwheel = e => { if (host.scrollWidth > host.clientWidth + 1) { host.scrollLeft += (e.deltaY || 0) + (e.deltaX || 0); e.preventDefault(); } }; + // 鼠标按住拖拽 → 平移时间轴(拖动超过阈值则不算点击,避免误选) + let dragging = false, dragX = 0, dragScroll = 0, moved = false; + host.onmousedown = e => { if (e.button !== 0) return; dragging = true; moved = false; dragX = e.clientX; dragScroll = host.scrollLeft; }; + host.onmousemove = e => { if (!dragging) return; const dx = e.clientX - dragX; if (Math.abs(dx) > 3) { moved = true; host.style.cursor = "grabbing"; } host.scrollLeft = dragScroll - dx; e.preventDefault(); }; + const endDrag = () => { dragging = false; host.style.cursor = ""; }; + host.onmouseup = endDrag; host.onmouseleave = endDrag; const W = model.total * PX; const ruler = document.createElement("div"); ruler.className = "tl-ruler"; ruler.style.width = W + "px"; for (let s = 0; s <= Math.ceil(model.total); s++) { @@ -335,7 +341,7 @@ el.textContent = c.label; el.dataset.start = c.start; el.dataset.end = c.start + c.dur; if (c.nodeId && S.nodes[c.nodeId]) el.classList.add("startable"); - el.onclick = e => { e.stopPropagation(); selectClip(c, el); }; // 单击=选中(不跳时间,避免视图乱滚) + el.onclick = e => { e.stopPropagation(); if (moved) return; selectClip(c, el); }; // 拖动后不算选中 el.ondblclick = e => { e.stopPropagation(); e.preventDefault(); if (c.nodeId && S.nodes[c.nodeId]) startFrom(c.nodeId, true); }; lane.appendChild(el); }); @@ -343,6 +349,7 @@ }); const ph = document.createElement("div"); ph.className = "tl-playhead"; host.appendChild(ph); els.playhead = ph; host.onclick = e => { + if (moved) return; // 刚才是拖拽,不当作跳转 const rect = host.getBoundingClientRect(); seek(Math.max(0, Math.min(model.total, (e.clientX - rect.left + host.scrollLeft) / PX))); };