Files
audio-ui/components/editor/ZoomControls.tsx
Sebastian Krüger 5cf9a69056 feat: complete Phase 3 - Advanced waveform visualization and zoom controls
Phase 3 Complete Features:
 Drag-to-scrub audio functionality
 Horizontal zoom (1x-20x) with smooth scaling
 Vertical amplitude zoom (0.5x-5x)
 Horizontal scrolling for zoomed waveform
 Grid lines every second for time reference
 Viewport culling for better performance
 Zoom controls UI component

Components Added:
- ZoomControls: Complete zoom control panel with:
  - Horizontal zoom slider and buttons
  - Amplitude zoom slider
  - Zoom in/out buttons
  - Fit to view button
  - Real-time zoom level display

Waveform Enhancements:
- Drag-to-scrub: Click and drag to scrub through audio
- Zoom support: View waveform at different zoom levels
- Scroll support: Navigate through zoomed waveform
- Grid lines: Visual time markers every second
- Viewport culling: Only render visible portions
- Cursor feedback: Grabbing cursor when dragging

AudioEditor Updates:
- Integrated zoom and scroll state management
- Auto-reset zoom on file clear
- Scroll slider appears when zoomed
- Smooth zoom transitions

Technical Improvements:
- Viewport culling: Only render visible waveform portions
- Grid rendering: Time-aligned vertical grid lines
- Smart scroll clamping: Prevent scrolling beyond bounds
- Zoom-aware seeking: Accurate time calculation with zoom
- Performance optimized rendering

Features Working:
 Drag waveform to scrub audio
 Zoom in up to 20x for detailed editing
 Adjust amplitude for better visualization
 Scroll through zoomed waveform
 Grid lines show time markers
 Smooth cursor interactions

Phase 3 Status: 95% complete
- Completed: All major features
- Optional: Measure/beat markers, OffscreenCanvas, Web Workers

Build verified and working ✓

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 15:44:29 +01:00

107 lines
2.8 KiB
TypeScript

'use client';
import * as React from 'react';
import { ZoomIn, ZoomOut, Maximize2, ChevronsUpDown, ChevronsLeftRight } from 'lucide-react';
import { Button } from '@/components/ui/Button';
import { Slider } from '@/components/ui/Slider';
import { cn } from '@/lib/utils/cn';
export interface ZoomControlsProps {
zoom: number;
onZoomChange: (zoom: number) => void;
amplitudeScale: number;
onAmplitudeScaleChange: (scale: number) => void;
onZoomIn: () => void;
onZoomOut: () => void;
onFitToView: () => void;
className?: string;
}
export function ZoomControls({
zoom,
onZoomChange,
amplitudeScale,
onAmplitudeScaleChange,
onZoomIn,
onZoomOut,
onFitToView,
className,
}: ZoomControlsProps) {
return (
<div className={cn('space-y-4', className)}>
{/* Horizontal Zoom */}
<div className="space-y-2">
<div className="flex items-center justify-between">
<label className="text-sm font-medium text-foreground flex items-center gap-2">
<ChevronsLeftRight className="h-4 w-4" />
Horizontal Zoom
</label>
<span className="text-sm text-muted-foreground">{zoom.toFixed(1)}x</span>
</div>
<div className="flex items-center gap-2">
<Button
variant="outline"
size="icon"
onClick={onZoomOut}
disabled={zoom <= 1}
title="Zoom Out"
className="h-8 w-8"
>
<ZoomOut className="h-4 w-4" />
</Button>
<Slider
value={zoom}
onChange={onZoomChange}
min={1}
max={20}
step={0.5}
className="flex-1"
/>
<Button
variant="outline"
size="icon"
onClick={onZoomIn}
disabled={zoom >= 20}
title="Zoom In"
className="h-8 w-8"
>
<ZoomIn className="h-4 w-4" />
</Button>
<Button
variant="outline"
size="icon"
onClick={onFitToView}
title="Fit to View"
className="h-8 w-8"
>
<Maximize2 className="h-4 w-4" />
</Button>
</div>
</div>
{/* Vertical (Amplitude) Zoom */}
<div className="space-y-2">
<div className="flex items-center justify-between">
<label className="text-sm font-medium text-foreground flex items-center gap-2">
<ChevronsUpDown className="h-4 w-4" />
Amplitude Zoom
</label>
<span className="text-sm text-muted-foreground">{amplitudeScale.toFixed(1)}x</span>
</div>
<Slider
value={amplitudeScale}
onChange={onAmplitudeScaleChange}
min={0.5}
max={5}
step={0.1}
/>
</div>
</div>
);
}