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>
This commit is contained in:
106
components/editor/ZoomControls.tsx
Normal file
106
components/editor/ZoomControls.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
'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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user