feat: implement time snapping to 100ms intervals for improved sound placement accuracy

This commit is contained in:
JSC
2025-09-03 21:03:28 +02:00
parent d4b87aafe3
commit cd7af24831
2 changed files with 41 additions and 12 deletions

View File

@@ -79,6 +79,14 @@ export function SequencerPage() {
}
}, [])
// Helper function to snap time to 100ms intervals with improved precision
const snapToGrid = useCallback((timeInSeconds: number): number => {
const snapIntervalMs = 100 // 100ms snap interval
const timeInMs = Math.max(0, timeInSeconds * 1000) // Ensure non-negative
const snappedMs = Math.round(timeInMs / snapIntervalMs) * snapIntervalMs
return Math.max(0, snappedMs / 1000) // Convert back to seconds, ensure non-negative
}, [])
// Update drag over info based on current mouse position and over target
useEffect(() => {
if (draggedItem && currentMousePos && (draggedItem.type === 'sound' || draggedItem.type === 'placed-sound')) {
@@ -93,8 +101,12 @@ export function SequencerPage() {
currentMousePos.y >= rect.top &&
currentMousePos.y <= rect.bottom
) {
const x = currentMousePos.x - rect.left
setDragOverInfo({ trackId: track.id, x: Math.max(0, x) })
const rawX = currentMousePos.x - rect.left
// Apply snapping to the drag over position for consistency
const rawTimeSeconds = rawX / state.zoom
const snappedTimeSeconds = snapToGrid(rawTimeSeconds)
const snappedX = snappedTimeSeconds * state.zoom
setDragOverInfo({ trackId: track.id, x: Math.max(0, snappedX) })
return
}
}
@@ -104,7 +116,7 @@ export function SequencerPage() {
} else {
setDragOverInfo(null)
}
}, [draggedItem, currentMousePos, state.tracks])
}, [draggedItem, currentMousePos, state.tracks, state.zoom, snapToGrid])
const handleDragEnd = useCallback((event: DragEndEvent) => {
const { active, over } = event
@@ -113,10 +125,10 @@ export function SequencerPage() {
// Handle sound drop from library to track
if (dragData?.type === 'sound' && overData?.type === 'track') {
// Use precise drop position if available (convert from pixels to milliseconds)
// Use precise drop position if available (dragOverInfo.x is already snapped)
let startTime = 0
if (dragOverInfo && dragOverInfo.trackId === overData.trackId) {
startTime = Math.max(0, (dragOverInfo.x / state.zoom) * 1000) // Convert seconds to milliseconds
startTime = Math.max(0, (dragOverInfo.x / state.zoom) * 1000) // Convert to milliseconds
}
const soundDuration = dragData.sound.duration // Already in milliseconds
@@ -149,10 +161,10 @@ export function SequencerPage() {
// Handle moving placed sounds within tracks
if (dragData?.type === 'placed-sound' && overData?.type === 'track') {
// Use precise drop position if available (convert from pixels to milliseconds)
// Use precise drop position if available (dragOverInfo.x is already snapped)
let startTime = dragData.startTime || 0
if (dragOverInfo && dragOverInfo.trackId === overData.trackId) {
startTime = Math.max(0, (dragOverInfo.x / state.zoom) * 1000) // Convert seconds to milliseconds
startTime = Math.max(0, (dragOverInfo.x / state.zoom) * 1000) // Convert to milliseconds
}
// Restrict placement to within track duration (all in milliseconds)
@@ -170,7 +182,10 @@ export function SequencerPage() {
if (track.id === sourceTrackId && sourceTrackId === targetTrackId) {
// Moving within the same track - just update position
const updatedSound: PlacedSound = {
...dragData,
id: dragData.id,
soundId: dragData.soundId,
name: dragData.name,
duration: dragData.duration,
startTime,
trackId: targetTrackId,
}
@@ -189,7 +204,10 @@ export function SequencerPage() {
} else if (track.id === targetTrackId) {
// Add to target track (different track move)
const updatedSound: PlacedSound = {
...dragData,
id: dragData.id,
soundId: dragData.soundId,
name: dragData.name,
duration: dragData.duration,
startTime,
trackId: targetTrackId,
}