feat(04-02): wire block interactions into TimelineCanvas
- Import CommandHistory and all 4 command classes from new modules - hitTest() with 8px EDGE for resize-left/resize-right/move zones - _hasOverlap() check for time range collision on same track - snapToBeat() with 0.1s threshold and calibrationOffset - _setupInteractions() with mousedown/mousemove/keydown listeners - _startMove(): live drag preview, snap on mouseup, overlap rejection - _startResize(): left/right edge handles, 0.5s min duration, snap - handleDrop(): place block from palette with crypto.randomUUID() - Keyboard: Delete, Ctrl+Z (undo), Ctrl+Shift+Z / Ctrl+Y (redo) - app.js: dragstart on palette, dragover/dragleave/drop on canvas - Ghost block rendered during dragover for visual feedback - Resize handle indicators drawn on selected block
This commit is contained in:
@@ -304,6 +304,48 @@ function initTimeline() {
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// Drag from palette to canvas
|
||||
const canvasEl = document.getElementById('timeline-canvas');
|
||||
|
||||
palette?.addEventListener('dragstart', (e) => {
|
||||
const tile = e.target.closest('.anim-tile');
|
||||
if (!tile) return;
|
||||
e.dataTransfer.setData('text/plain', tile.dataset.anim);
|
||||
e.dataTransfer.effectAllowed = 'copy';
|
||||
});
|
||||
|
||||
canvasEl?.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
// Show ghost preview for current drag position
|
||||
const rect = canvasEl.getBoundingClientRect();
|
||||
const mx = e.clientX - rect.left;
|
||||
const my = e.clientY - rect.top;
|
||||
const trackIdx = timeline.yToTrackIndex(my);
|
||||
const time = timeline.xToTime(mx);
|
||||
timeline._dragState = {
|
||||
ghost: true,
|
||||
trackIdx: trackIdx,
|
||||
time: Math.max(0, time),
|
||||
duration: 4.0
|
||||
};
|
||||
});
|
||||
|
||||
canvasEl?.addEventListener('dragleave', () => {
|
||||
timeline._dragState = null;
|
||||
});
|
||||
|
||||
canvasEl?.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
const animType = e.dataTransfer.getData('text/plain');
|
||||
if (!animType) return;
|
||||
const rect = canvasEl.getBoundingClientRect();
|
||||
const mx = e.clientX - rect.left;
|
||||
const my = e.clientY - rect.top;
|
||||
timeline.handleDrop(animType, mx, my);
|
||||
timeline._dragState = null;
|
||||
});
|
||||
|
||||
timeline.startRenderLoop();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user