Files
audio-ui/components/editor/ZoomControls.tsx

107 lines
2.8 KiB
TypeScript
Raw Normal View History

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
'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>
);
}