fix: timeline zoom and waveform rendering improvements
- Fix timeline width calculation to always fill viewport at minimum - Fix waveform sampling to match timeline width calculation exactly - Fix infinite scroll loop by removing circular callback - Ensure scrollbars appear correctly when zooming in - Use consistent PIXELS_PER_SECOND_BASE = 5 across all components 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -42,10 +42,16 @@ export function TimeScale({
|
|||||||
|
|
||||||
// Calculate total timeline width (match waveform calculation)
|
// Calculate total timeline width (match waveform calculation)
|
||||||
// Uses 5 pixels per second as base scale, multiplied by zoom
|
// Uses 5 pixels per second as base scale, multiplied by zoom
|
||||||
|
// Always ensure minimum width is at least viewport width for full coverage
|
||||||
const PIXELS_PER_SECOND_BASE = 5;
|
const PIXELS_PER_SECOND_BASE = 5;
|
||||||
const totalWidth = React.useMemo(() => {
|
const totalWidth = React.useMemo(() => {
|
||||||
return duration * zoom * PIXELS_PER_SECOND_BASE;
|
if (zoom >= 1) {
|
||||||
}, [duration, zoom]);
|
const calculatedWidth = duration * zoom * PIXELS_PER_SECOND_BASE;
|
||||||
|
// Ensure it's at least viewport width so timeline always fills
|
||||||
|
return Math.max(calculatedWidth, viewportWidth);
|
||||||
|
}
|
||||||
|
return viewportWidth;
|
||||||
|
}, [duration, zoom, viewportWidth]);
|
||||||
|
|
||||||
// Update viewport width on resize
|
// Update viewport width on resize
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
|||||||
@@ -329,7 +329,17 @@ export function Track({
|
|||||||
|
|
||||||
const buffer = track.audioBuffer;
|
const buffer = track.audioBuffer;
|
||||||
const channelData = buffer.getChannelData(0);
|
const channelData = buffer.getChannelData(0);
|
||||||
const samplesPerPixel = Math.floor(buffer.length / (width * zoom));
|
// Calculate samples per pixel based on the total width
|
||||||
|
// Must match the timeline calculation exactly
|
||||||
|
const PIXELS_PER_SECOND_BASE = 5;
|
||||||
|
let totalWidth;
|
||||||
|
if (zoom >= 1) {
|
||||||
|
const calculatedWidth = duration * zoom * PIXELS_PER_SECOND_BASE;
|
||||||
|
totalWidth = Math.max(calculatedWidth, width);
|
||||||
|
} else {
|
||||||
|
totalWidth = width;
|
||||||
|
}
|
||||||
|
const samplesPerPixel = buffer.length / totalWidth;
|
||||||
|
|
||||||
// Draw waveform
|
// Draw waveform
|
||||||
ctx.fillStyle = track.color;
|
ctx.fillStyle = track.color;
|
||||||
|
|||||||
@@ -170,12 +170,7 @@ export function TrackList({
|
|||||||
});
|
});
|
||||||
|
|
||||||
setSyncingScroll(false);
|
setSyncingScroll(false);
|
||||||
|
}, [syncingScroll]);
|
||||||
// Also call the external callback if provided
|
|
||||||
if (onTimeScaleScroll) {
|
|
||||||
onTimeScaleScroll();
|
|
||||||
}
|
|
||||||
}, [syncingScroll, onTimeScaleScroll]);
|
|
||||||
|
|
||||||
// Expose the scroll handler via ref so AudioEditor can call it
|
// Expose the scroll handler via ref so AudioEditor can call it
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user