Improve overview selection behaviour
This commit is contained in:
parent
951abb608d
commit
8cade215af
|
@ -238,7 +238,6 @@ export const Waveform: React.FC<Props> = ({ audioContext }: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
video.pause();
|
video.pause();
|
||||||
console.log('paused video');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleZoomIn = () => {
|
const handleZoomIn = () => {
|
||||||
|
@ -267,15 +266,36 @@ export const Waveform: React.FC<Props> = ({ audioContext }: Props) => {
|
||||||
setZoomSettings(settings);
|
setZoomSettings(settings);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSelectionStart = (x: number) => {
|
||||||
|
const video = videoRef.current;
|
||||||
|
if (video == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const frame = canvasXToFrame(x, mediaSet.audio.frames);
|
||||||
|
if (video.paused) {
|
||||||
|
video.currentTime = frame / mediaSet.audio.sampleRate;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleSelectionChange = (selection: Selection) => {
|
const handleSelectionChange = (selection: Selection) => {
|
||||||
|
console.log('in handleSelectionChange handler');
|
||||||
if (mediaSet == null) {
|
if (mediaSet == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const startFrame = canvasXToFrame(selection.x1, mediaSet.audio.frames);
|
||||||
|
const endFrame = canvasXToFrame(selection.x2, mediaSet.audio.frames);
|
||||||
const settings: ZoomSettings = {
|
const settings: ZoomSettings = {
|
||||||
startFrame: canvasXToFrame(selection.x1, mediaSet.audio.frames),
|
startFrame: startFrame,
|
||||||
endFrame: canvasXToFrame(selection.x2, mediaSet.audio.frames),
|
endFrame: endFrame,
|
||||||
};
|
};
|
||||||
setZoomSettings(settings);
|
setZoomSettings(settings);
|
||||||
|
|
||||||
|
const video = videoRef.current;
|
||||||
|
if (video == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
video.currentTime = startFrame / mediaSet.audio.sampleRate;
|
||||||
};
|
};
|
||||||
|
|
||||||
// render component:
|
// render component:
|
||||||
|
@ -335,6 +355,7 @@ export const Waveform: React.FC<Props> = ({ audioContext }: Props) => {
|
||||||
peaks={overviewPeaks}
|
peaks={overviewPeaks}
|
||||||
numFrames={mediaSet.audio.frames}
|
numFrames={mediaSet.audio.frames}
|
||||||
style={overviewStyles}
|
style={overviewStyles}
|
||||||
|
onSelectionStart={handleSelectionStart}
|
||||||
onSelectionChange={handleSelectionChange}
|
onSelectionChange={handleSelectionChange}
|
||||||
></WaveformOverview>
|
></WaveformOverview>
|
||||||
<div style={wrapperProps}>
|
<div style={wrapperProps}>
|
||||||
|
|
|
@ -16,12 +16,6 @@ export const canvasXToFrame = (x: number, numFrames: number): number => {
|
||||||
return Math.floor((x / CanvasLogicalWidth) * numFrames);
|
return Math.floor((x / CanvasLogicalWidth) * numFrames);
|
||||||
};
|
};
|
||||||
|
|
||||||
// // TODO: add tests
|
|
||||||
// export const canvasXToSecs = (x: number, numFrames: number): number => {
|
|
||||||
// const frame := canvasXToFrame(x, numFrames)
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TODO: add tests
|
// TODO: add tests
|
||||||
export const secsToCanvasX = (
|
export const secsToCanvasX = (
|
||||||
secs: number,
|
secs: number,
|
||||||
|
|
|
@ -11,6 +11,7 @@ type Props = {
|
||||||
peaks: number[][] | null;
|
peaks: number[][] | null;
|
||||||
numFrames: number;
|
numFrames: number;
|
||||||
style: React.CSSProperties;
|
style: React.CSSProperties;
|
||||||
|
onSelectionStart: (x1: number) => void;
|
||||||
onSelectionChange: (selection: Selection) => void;
|
onSelectionChange: (selection: Selection) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,17 +20,21 @@ enum Mode {
|
||||||
Selecting,
|
Selecting,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: render position marker during playback
|
||||||
export const Waveform: React.FC<Props> = (props: Props) => {
|
export const Waveform: React.FC<Props> = (props: Props) => {
|
||||||
const hudCanvasRef = useRef<HTMLCanvasElement>(null);
|
const hudCanvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
const [mode, setMode] = useState(Mode.Normal);
|
const [mode, setMode] = useState(Mode.Normal);
|
||||||
|
|
||||||
const defaultSelection: Selection = { x1: 0, x2: 0 };
|
const defaultSelection: Selection = { x1: 0, x2: 0 };
|
||||||
|
// selection is the current selection in canvas coordinates:
|
||||||
const [selection, setSelection] = useState(defaultSelection);
|
const [selection, setSelection] = useState(defaultSelection);
|
||||||
// TODO: is it needed?
|
// newSelection is a new selection in the process of being drawn by the user.
|
||||||
const [newStartSelection, setNewStartSelection] = useState(0);
|
// It is only useful if Mode.Selecting is active.
|
||||||
|
const [newSelection, setNewSelection] = useState(defaultSelection);
|
||||||
|
|
||||||
// effects
|
// effects
|
||||||
|
|
||||||
|
// draw the overview waveform
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async function () {
|
(async function () {
|
||||||
const canvas = hudCanvasRef.current;
|
const canvas = hudCanvasRef.current;
|
||||||
|
@ -46,7 +51,13 @@ export const Waveform: React.FC<Props> = (props: Props) => {
|
||||||
|
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
if (selection.x1 >= selection.x2) {
|
let currentSelection: Selection;
|
||||||
|
if (mode == Mode.Selecting) {
|
||||||
|
currentSelection = newSelection;
|
||||||
|
} else {
|
||||||
|
currentSelection = selection;
|
||||||
|
}
|
||||||
|
if (currentSelection.x1 >= currentSelection.x2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +65,12 @@ export const Waveform: React.FC<Props> = (props: Props) => {
|
||||||
ctx.strokeStyle = 'red';
|
ctx.strokeStyle = 'red';
|
||||||
ctx.lineWidth = 2;
|
ctx.lineWidth = 2;
|
||||||
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
|
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
|
||||||
ctx.rect(selection.x1, 2, selection.x2 - selection.x1, canvas.height - 8);
|
ctx.rect(
|
||||||
|
currentSelection.x1,
|
||||||
|
2,
|
||||||
|
currentSelection.x2 - currentSelection.x1,
|
||||||
|
canvas.height - 8
|
||||||
|
);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
})();
|
})();
|
||||||
|
@ -63,35 +79,41 @@ export const Waveform: React.FC<Props> = (props: Props) => {
|
||||||
// handlers
|
// handlers
|
||||||
|
|
||||||
const handleMouseDown = (evt: MouseEvent<HTMLCanvasElement>) => {
|
const handleMouseDown = (evt: MouseEvent<HTMLCanvasElement>) => {
|
||||||
console.log('mousedown');
|
|
||||||
if (mode != Mode.Normal) {
|
if (mode != Mode.Normal) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setMode(Mode.Selecting);
|
setMode(Mode.Selecting);
|
||||||
setNewStartSelection(mouseEventToCanvasX(evt));
|
|
||||||
|
const x = mouseEventToCanvasX(evt);
|
||||||
|
setNewSelection({ x1: x, x2: x });
|
||||||
|
props.onSelectionStart(x);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseMove = (evt: MouseEvent<HTMLCanvasElement>) => {
|
const handleMouseMove = (evt: MouseEvent<HTMLCanvasElement>) => {
|
||||||
console.log('mousemove');
|
|
||||||
if (mode != Mode.Selecting) {
|
if (mode != Mode.Selecting) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selection: Selection = {
|
const x = mouseEventToCanvasX(evt);
|
||||||
x1: newStartSelection,
|
if (x == newSelection.x2) {
|
||||||
x2: mouseEventToCanvasX(evt),
|
return;
|
||||||
};
|
}
|
||||||
setSelection(selection);
|
|
||||||
|
setNewSelection({ ...newSelection, x2: x });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseUp = () => {
|
const handleMouseUp = () => {
|
||||||
console.log('mouseup');
|
|
||||||
if (mode != Mode.Selecting) {
|
if (mode != Mode.Selecting) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setMode(Mode.Normal);
|
setMode(Mode.Normal);
|
||||||
props.onSelectionChange(selection);
|
|
||||||
|
// TODO: better shallow equality check?
|
||||||
|
if (selection.x1 !== newSelection.x1 || selection.x2 !== newSelection.x2) {
|
||||||
|
setSelection(newSelection);
|
||||||
|
props.onSelectionChange(newSelection);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// render component
|
// render component
|
||||||
|
|
Loading…
Reference in New Issue