import { useEffect, useState, useRef, MouseEvent } from 'react'; import { Canvas as WaveformCanvas } from './Canvas'; import { CanvasLogicalWidth, CanvasLogicalHeight, Selection } from './Waveform'; import { mouseEventToCanvasX } from './Helpers'; interface Props { peaks: number[][] | null; numFrames: number; style: React.CSSProperties; onSelectionStart: (x1: number) => void; onSelectionChange: (selection: Selection) => void; } enum Mode { Normal, Selecting, } // TODO: render position marker during playback export const Waveform: React.FC = (props: Props) => { const hudCanvasRef = useRef(null); const [mode, setMode] = useState(Mode.Normal); const defaultSelection: Selection = { x1: 0, x2: 0 }; // selection is the current selection in canvas coordinates: const [selection, setSelection] = useState(defaultSelection); // newSelection is a new selection in the process of being drawn by the user. // It is only useful if Mode.Selecting is active. const [newSelection, setNewSelection] = useState(defaultSelection); // effects // draw the overview waveform useEffect(() => { (async function () { const canvas = hudCanvasRef.current; if (canvas == null) { console.error('no hud canvas ref available'); return; } const ctx = canvas.getContext('2d'); if (ctx == null) { console.error('no hud 2d context available'); return; } ctx.clearRect(0, 0, canvas.width, canvas.height); let currentSelection: Selection; if (mode == Mode.Selecting) { currentSelection = newSelection; } else { currentSelection = selection; } if (currentSelection.x1 >= currentSelection.x2) { return; } ctx.beginPath(); ctx.strokeStyle = 'red'; ctx.lineWidth = 2; ctx.fillStyle = 'rgba(255, 255, 255, 0.3)'; ctx.rect( currentSelection.x1, 2, currentSelection.x2 - currentSelection.x1, canvas.height - 8 ); ctx.fill(); ctx.stroke(); })(); }); // handlers const handleMouseDown = (evt: MouseEvent) => { if (mode != Mode.Normal) { return; } setMode(Mode.Selecting); const x = mouseEventToCanvasX(evt); setNewSelection({ x1: x, x2: x }); props.onSelectionStart(x); }; const handleMouseMove = (evt: MouseEvent) => { if (mode != Mode.Selecting) { return; } const x = mouseEventToCanvasX(evt); if (x == newSelection.x2) { return; } setNewSelection({ ...newSelection, x2: x }); }; const handleMouseUp = () => { if (mode != Mode.Selecting) { return; } setMode(Mode.Normal); // TODO: better shallow equality check? if (selection.x1 !== newSelection.x1 || selection.x2 !== newSelection.x2) { setSelection(newSelection); props.onSelectionChange(newSelection); } }; // render component const canvasStyles = { width: '100%', height: '100%', margin: '0 auto', display: 'block', } as React.CSSProperties; const hudCanvasStyles = { width: '100%', height: '100%', position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, zIndex: 1, } as React.CSSProperties; return ( <>
); };