poc: Add SeekBar
This commit is contained in:
parent
d5df962627
commit
084cabaca9
|
@ -1,6 +1,11 @@
|
|||
import { CanvasLogicalWidth } from './Waveform';
|
||||
import { MouseEvent } from 'react';
|
||||
|
||||
interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
// TODO: add tests
|
||||
export const mouseEventToCanvasX = (
|
||||
evt: MouseEvent<HTMLCanvasElement>
|
||||
|
@ -10,6 +15,20 @@ export const mouseEventToCanvasX = (
|
|||
return (elementX * CanvasLogicalWidth) / rect.width;
|
||||
};
|
||||
|
||||
// TODO: add tests
|
||||
export const mouseEventToCanvasPoint = (
|
||||
evt: MouseEvent<HTMLCanvasElement>
|
||||
): Point => {
|
||||
const rect = evt.currentTarget.getBoundingClientRect();
|
||||
const elementX = evt.clientX - rect.left;
|
||||
const elementY = evt.clientY - rect.top;
|
||||
|
||||
return {
|
||||
x: (elementX * evt.currentTarget.width) / rect.width,
|
||||
y: (elementY * evt.currentTarget.height) / rect.height,
|
||||
};
|
||||
};
|
||||
|
||||
// TODO: add tests
|
||||
export const canvasXToFrame = (x: number, numFrames: number): number => {
|
||||
return Math.floor((x / CanvasLogicalWidth) * numFrames);
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import { useRef, useEffect, useState, MouseEvent } from 'react';
|
||||
import { mouseEventToCanvasPoint } from './Helpers';
|
||||
|
||||
interface Props {
|
||||
duration: number;
|
||||
style: React.CSSProperties;
|
||||
}
|
||||
|
||||
const LogicalHeight = 200;
|
||||
const MarginX = 0;
|
||||
const MarginY = 85;
|
||||
const KnobRadius = 40;
|
||||
|
||||
export const SeekBar: React.FC<Props> = (props: Props) => {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const [position, _setPosition] = useState(100);
|
||||
const [cursor, setCursor] = useState('auto');
|
||||
|
||||
const secsToCanvasX = (secs: number, width: number): number => {
|
||||
return (secs / props.duration) * width;
|
||||
};
|
||||
|
||||
// draw the canvas
|
||||
useEffect(() => {
|
||||
const canvas = canvasRef.current;
|
||||
if (canvas == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (ctx == null) {
|
||||
console.error('no seekbar 2d context available');
|
||||
return;
|
||||
}
|
||||
|
||||
// Set aspect ratio.
|
||||
canvas.width = canvas.height * (canvas.clientWidth / canvas.clientHeight);
|
||||
|
||||
ctx.fillStyle = '#333333';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
ctx.fillStyle = 'black';
|
||||
ctx.fillRect(
|
||||
MarginX,
|
||||
MarginY,
|
||||
canvas.width - MarginX * 2,
|
||||
canvas.height - MarginY * 2
|
||||
);
|
||||
|
||||
const x = secsToCanvasX(position, canvas.width);
|
||||
const y = LogicalHeight / 2;
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, KnobRadius, 0, 2 * Math.PI, false);
|
||||
ctx.fillStyle = 'red';
|
||||
ctx.fill();
|
||||
});
|
||||
|
||||
const style = { ...props.style, cursor: cursor };
|
||||
|
||||
// handlers
|
||||
|
||||
const handleMouseMove = (evt: MouseEvent<HTMLCanvasElement>) => {
|
||||
const { x: _x, y: y } = mouseEventToCanvasPoint(evt);
|
||||
// TODO: improve mouse detection around knob.
|
||||
if (y > MarginY && y < LogicalHeight - MarginY) {
|
||||
setCursor('pointer');
|
||||
} else {
|
||||
setCursor('auto');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<canvas
|
||||
style={style}
|
||||
ref={canvasRef}
|
||||
height={LogicalHeight}
|
||||
onMouseMove={handleMouseMove}
|
||||
></canvas>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -2,6 +2,7 @@ import { useEffect, useState, useRef, MouseEvent } from 'react';
|
|||
import { Waveform as WaveformOverview } from './Overview';
|
||||
import { Thumbnails } from './Thumbnails';
|
||||
import { Canvas as WaveformCanvas } from './Canvas';
|
||||
import { SeekBar } from './SeekBar';
|
||||
import { canvasXToFrame, mouseEventToCanvasX } from './Helpers';
|
||||
|
||||
interface Props {
|
||||
|
@ -323,6 +324,12 @@ export const Waveform: React.FC<Props> = ({ audioContext }: Props) => {
|
|||
margin: '10px auto 0 auto',
|
||||
display: 'block',
|
||||
};
|
||||
const seekBarStyles = {
|
||||
width: '90%',
|
||||
height: '50px',
|
||||
margin: '0 auto',
|
||||
display: 'block',
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -351,6 +358,10 @@ export const Waveform: React.FC<Props> = ({ audioContext }: Props) => {
|
|||
height={CanvasLogicalHeight}
|
||||
></canvas>
|
||||
</div>
|
||||
<SeekBar
|
||||
duration={mediaSet.audio.frames / mediaSet.audio.sampleRate}
|
||||
style={seekBarStyles}
|
||||
/>
|
||||
<div style={controlPanelStyles}>
|
||||
<button onClick={handlePlay}>Play</button>
|
||||
<button onClick={handlePause}>Pause</button>
|
||||
|
|
Loading…
Reference in New Issue