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>
107 lines
2.8 KiB
TypeScript
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>
|
|
);
|
|
}
|