Fix editor timeline interactions
This commit is contained in:
parent
de895dd7f8
commit
78539ec8b0
1 changed files with 41 additions and 5 deletions
|
|
@ -28,8 +28,11 @@
|
|||
playheadFrames: 0,
|
||||
activeTool: 'select', // 'select' | 'razor' | 'hand'
|
||||
selectedId: null,
|
||||
contextMenu: null,
|
||||
onClipsChanged: null, // callback(clips[])
|
||||
onPlayheadMoved: null, // callback(frames)
|
||||
onClipContextMenu: null, // callback({clip, x, y})
|
||||
onExternalDrop: null, // callback({track, timelineFrames, dataTransfer})
|
||||
};
|
||||
|
||||
let _uid = 0;
|
||||
|
|
@ -46,6 +49,8 @@
|
|||
s.scale = options.scale || 100;
|
||||
s.onClipsChanged = options.onClipsChanged || null;
|
||||
s.onPlayheadMoved = options.onPlayheadMoved || null;
|
||||
s.onClipContextMenu = options.onClipContextMenu || null;
|
||||
s.onExternalDrop = options.onExternalDrop || null;
|
||||
|
||||
container.innerHTML = '';
|
||||
container.style.cssText = [
|
||||
|
|
@ -96,6 +101,8 @@
|
|||
area.dataset.trackId = t.id;
|
||||
area.style.cssText = 'flex:1;position:relative;overflow:visible;';
|
||||
area.addEventListener('click', _onAreaClick);
|
||||
area.addEventListener('dragover', _onAreaDragOver);
|
||||
area.addEventListener('drop', _onAreaDrop);
|
||||
row.appendChild(area);
|
||||
|
||||
s.tracksEl.appendChild(row);
|
||||
|
|
@ -241,7 +248,7 @@
|
|||
|
||||
// Clip label
|
||||
var lbl = document.createElement('span');
|
||||
lbl.textContent = clip.display_name || 'Clip';
|
||||
lbl.textContent = clip.display_name || clip.name || 'Clip';
|
||||
lbl.style.cssText = [
|
||||
'font-size:9px', 'font-weight:500',
|
||||
'color:var(--text-secondary)',
|
||||
|
|
@ -250,6 +257,14 @@
|
|||
].join(';');
|
||||
el.appendChild(lbl);
|
||||
|
||||
el.addEventListener('contextmenu', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
s.selectedId = clip._id;
|
||||
_renderClips();
|
||||
if (s.onClipContextMenu) s.onClipContextMenu({ clip: Object.assign({}, clip), x: e.clientX, y: e.clientY });
|
||||
});
|
||||
|
||||
if (s.activeTool === 'select') {
|
||||
// Trim handles (shown on hover)
|
||||
var lh = _makeHandle('left');
|
||||
|
|
@ -268,6 +283,7 @@
|
|||
|
||||
// Drag to move (body only)
|
||||
el.addEventListener('mousedown', function (e) {
|
||||
if (e.button !== 0) return;
|
||||
if (e.target === lh || e.target === rh) return;
|
||||
e.preventDefault();
|
||||
s.selectedId = clip._id;
|
||||
|
|
@ -279,10 +295,12 @@
|
|||
});
|
||||
|
||||
lh.addEventListener('mousedown', function (e) {
|
||||
if (e.button !== 0) return;
|
||||
e.stopPropagation();
|
||||
_onTrimStart(e, clip, 'left');
|
||||
});
|
||||
rh.addEventListener('mousedown', function (e) {
|
||||
if (e.button !== 0) return;
|
||||
e.stopPropagation();
|
||||
_onTrimStart(e, clip, 'right');
|
||||
});
|
||||
|
|
@ -411,7 +429,7 @@
|
|||
if (s.onClipsChanged) s.onClipsChanged(s.clips.slice());
|
||||
}
|
||||
|
||||
// ── Area click (deselect) ───────────────────────────────────────────────────
|
||||
// ── Area click/drop ─────────────────────────────────────────────────────────
|
||||
|
||||
function _onAreaClick(e) {
|
||||
if (e.target !== e.currentTarget) return; // hit a clip, not empty space
|
||||
|
|
@ -421,6 +439,23 @@
|
|||
}
|
||||
}
|
||||
|
||||
function _onAreaDragOver(e) {
|
||||
if (!s.onExternalDrop) return;
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
|
||||
function _onAreaDrop(e) {
|
||||
if (!s.onExternalDrop) return;
|
||||
e.preventDefault();
|
||||
var area = e.currentTarget;
|
||||
var rect = area.getBoundingClientRect();
|
||||
var x = e.clientX - rect.left + s.container.scrollLeft;
|
||||
var track = Number(area.dataset.trackId);
|
||||
var timelineFrames = Math.max(0, pxToFrames(x));
|
||||
s.onExternalDrop({ track: track, timelineFrames: timelineFrames, dataTransfer: e.dataTransfer });
|
||||
}
|
||||
|
||||
// ── Keyboard ────────────────────────────────────────────────────────────────
|
||||
|
||||
function _onKeyDown(e) {
|
||||
|
|
@ -515,6 +550,7 @@
|
|||
|
||||
s.tracksEl.addEventListener('mousedown', function (e) {
|
||||
if (s.activeTool !== 'hand') return;
|
||||
if (e.button !== 0) return;
|
||||
dragging = true;
|
||||
startX = e.clientX;
|
||||
startScroll = s.container.scrollLeft;
|
||||
|
|
@ -537,7 +573,7 @@
|
|||
|
||||
// ── Add clip at playhead ─────────────────────────────────────────────────────
|
||||
|
||||
function addClip(asset, srcIn, srcOut, track) {
|
||||
function addClip(asset, srcIn, srcOut, track, timelineInFrames) {
|
||||
track = track !== undefined ? track : 0;
|
||||
srcIn = srcIn || 0;
|
||||
srcOut = srcOut || (asset.duration_ms ? asset.duration_ms / 1000 : 10);
|
||||
|
|
@ -546,13 +582,13 @@
|
|||
// regardless of sequence frame rate (not hardcoded to 59.94).
|
||||
var srcInFr = Math.round(srcIn * s.fps);
|
||||
var srcOutFr = Math.round(srcOut * s.fps);
|
||||
var tlInFr = s.playheadFrames;
|
||||
var tlInFr = timelineInFrames !== undefined ? Math.max(0, Math.round(timelineInFrames)) : s.playheadFrames;
|
||||
var tlOutFr = tlInFr + (srcOutFr - srcInFr);
|
||||
|
||||
var clip = {
|
||||
_id: uid(),
|
||||
asset_id: asset.id || asset.asset_id,
|
||||
display_name: asset.display_name || asset.filename || 'Clip',
|
||||
display_name: asset.display_name || asset.filename || asset.name || 'Clip',
|
||||
duration_ms: asset.duration_ms || null,
|
||||
streamUrl: asset.streamUrl || null,
|
||||
track: track,
|
||||
|
|
|
|||
Loading…
Reference in a new issue