import { memo, useEffect, useState, useCallback, useRef } from 'react'; import { ffmpegExtractWindow } from '../util/constants'; import { RenderableWaveform } from '../types'; const BigWaveform = memo(({ waveforms, relevantTime, playing, durationSafe, zoom, seekRel }: { waveforms: RenderableWaveform[], relevantTime: number, playing: boolean, durationSafe: number, zoom: number, seekRel: (a: number) => void, }) => { const windowSize = ffmpegExtractWindow * 2; const windowStart = Math.max(0, relevantTime - windowSize); const windowEnd = relevantTime + windowSize; const filtered = waveforms.filter((waveform) => waveform.from >= windowStart && waveform.to <= windowEnd); const scaleFactor = zoom; const [smoothTimeRaw, setSmoothTime] = useState(relevantTime); const smoothTime = smoothTimeRaw ?? relevantTime; const mouseDownRef = useRef<{ relevantTime: number, x }>(); const containerRef = useRef(null); const getRect = useCallback(() => containerRef.current!.getBoundingClientRect(), []); const handleMouseDown = useCallback((e) => { const rect = e.target.getBoundingClientRect(); const x = e.clientX - rect.left; mouseDownRef.current = { relevantTime, x }; e.preventDefault(); }, [relevantTime]); const scaleToTime = useCallback((v) => (((v) / getRect().width) * windowSize) / zoom, [getRect, windowSize, zoom]); const handleMouseMove = useCallback((e) => { if (mouseDownRef.current == null) return; seekRel(-scaleToTime(e.movementX)); e.preventDefault(); }, [scaleToTime, seekRel]); const handleWheel = useCallback((e) => { seekRel(scaleToTime(e.deltaX)); }, [scaleToTime, seekRel]); const handleMouseUp = useCallback((e) => { if (!mouseDownRef.current) return; mouseDownRef.current = undefined; e.preventDefault(); }, []); useEffect(() => { const startTime = Date.now(); if (playing) { let raf; // eslint-disable-next-line no-inner-declarations function render() { raf = window.requestAnimationFrame(() => { setSmoothTime(relevantTime + (Date.now() - startTime) / 1000); render(); }); } render(); return () => window.cancelAnimationFrame(raf); } setSmoothTime(undefined); return undefined; }, [relevantTime, playing]); return ( // eslint-disable-next-line jsx-a11y/no-static-element-interactions
{filtered.map((waveform) => { const left = 0.5 + ((waveform.from - smoothTime) / windowSize) * scaleFactor; const width = ((waveform.to - waveform.from) / windowSize) * scaleFactor; const leftPercent = `${left * 100}%`; const widthPercent = `${width * 100}%`; return ( = durationSafe ? '1px solid var(--gray11)' : undefined, }} /> ); })}
); }); export default BigWaveform;