newsmy_recorder/src/components/ProgressBar.js

172 lines
5.3 KiB
JavaScript

import { useRef, useCallback, useState, useEffect } from "react";
const pointWidth = 2;
const pointMargin = 3;
const intervalsPerTag = 10;
function timeTag(timepoint) {
if (isNaN(timepoint)) return "00:00";
timepoint = Math.round(timepoint);
let second = Math.round(timepoint % 60);
let minute = Math.floor(timepoint / 60);
return minute.toString().padStart(2, '0') + ":" + second.toString().padStart(2, '0');
}
const pointCoordinates = ({
index, pointWidth, pointMargin, canvasHeight, maxAmplitude, amplitude,
}) => {
let pointHeight = Math.round((amplitude / 100) * maxAmplitude)
if (pointHeight < 3) pointHeight = 3;
const verticalCenter = Math.round((canvasHeight - pointHeight) / 2)
return [
index * (pointWidth + pointMargin), // x starting point
(canvasHeight - pointHeight) - verticalCenter + 10, // y starting point
pointWidth, // width
pointHeight, // height
]
}
function drawText(context, duration, interval) {
context.save();
context.fillStyle = "red";
context.textBaseline = "top";
context.font = "12px arial";
context.fillStyle = "#99A3AF";
let scales = Math.floor(duration / interval);
scales = Math.ceil(scales / intervalsPerTag) * intervalsPerTag + 1;
for (let i = 0; i < scales; i++) {
context.fillStyle = "#9DA7B2";
context.fillRect(i * (pointWidth + pointMargin), 0, pointWidth, (i % intervalsPerTag) == 0 ? 12 : 6);
}
context.fillStyle = "#99A3AF";
let timepoint = 0;
for (let i = 0; i <= scales; i++) {
if (i % intervalsPerTag == 0) {
context.fillText(timeTag(timepoint / 1000), i * (pointWidth + pointMargin) - 14, 10 + 4);
}
timepoint += interval;
}
context.restore();
}
const paintCanvas = ({
canvas, waveformData, duration, scrollLeft, leftPadding, canvasHeight, pointWidth, pointMargin, interval
}) => {
// console.log("paintCanvas", duration, canvasHeight, canvas.width, scrollLeft);
const context = canvas.getContext('2d');
context.save();
context.clearRect(0, 0, canvas.width, canvas.height);
context.translate(leftPadding, 0);;
drawText(context, duration, interval); // 画刻度尺
waveformData.forEach((p, i) => {
context.beginPath()
const coordinates = pointCoordinates({
index: i,
pointWidth,
pointMargin,
canvasHeight,
maxAmplitude: canvasHeight - 30, // 留出空间画时间轴
amplitude: p,
})
context.rect(...coordinates)
context.fillStyle = (coordinates[0] <= scrollLeft) ? '#FF595A' : '#ABB5BC'
context.fill()
});
context.restore();
}
// duration ms
export default function ({ width, duration, currentTime, playing, seek, waveData }) {
const interval = (duration > 20 * 60 * 1000) ? 200 : 100; // ms
const container = useRef(null);
const canvas = useRef(null);
const [scrollLeft, setScrollLeft] = useState(0);
const [mouseDown, setMouseDown] = useState(false);
const [clickInfo, setClickInfo] = useState({
startX: 0,
scrollLeft: 0,
});
const onMouseDown = (event) => {
setMouseDown(true);
setClickInfo({
startX: event.pageX - container.current.offsetLeft,
scrollLeft: container.current.scrollLeft,
});
}
const onMouseLeave = (event) => {
setMouseDown(false);
}
const onMouseUp = (event) => {
setMouseDown(false);
seek(scrollLeft * interval / (pointWidth + pointMargin) / 1000);
}
const onMouseMove = (event) => {
event.preventDefault();
if (!mouseDown) return;
const x = event.pageX - container.current.offsetLeft;
const scroll = x - clickInfo.startX;
container.current.scrollLeft = clickInfo.scrollLeft - scroll;
}
const onScroll = () => {
setScrollLeft(Math.round(container.current.scrollLeft));
};
const paintWaveform = useCallback(() => {
paintCanvas({
canvas: canvas.current,
waveformData: waveData,
duration: duration,
scrollLeft: scrollLeft,
leftPadding: Math.round(width / 2),
canvasHeight: canvas.current.height,
pointWidth,
pointMargin,
interval,
})
}, [duration, width, scrollLeft, waveData])
useEffect(() => {
if (!canvas.current) return;
paintWaveform()
}, [canvas, width, duration, scrollLeft, waveData])
useEffect(() => {
if (!mouseDown)
container.current.scrollLeft = Math.round(currentTime * 1000 / interval * (pointWidth + pointMargin));
}, [currentTime]);
return <div ref={container}
onMouseDown={onMouseDown}
onMouseUp={onMouseUp}
onMouseMove={onMouseMove}
onMouseLeave={onMouseLeave}
onScroll={onScroll}
style={{
width: width,
overflow: "scroll",
overflowY: "hidden",
}}>
<div style={{
height: 60,
backgroundColor: "red",
width: 2,
position: "absolute",
left: width / 2 + 70,
margin: 0,
padding: 0,
}} />
<canvas ref={canvas}
height={60}
width={width + (duration / interval) * (pointWidth + pointMargin)}
/>
</div>
}