diff --git a/src/components/audio-animation/index.less b/src/components/audio-animation/index.less index c2662174..c16c3d32 100644 --- a/src/components/audio-animation/index.less +++ b/src/components/audio-animation/index.less @@ -6,5 +6,6 @@ canvas { display: block; + image-rendering: crisp-edges; } } diff --git a/src/components/audio-animation/index.tsx b/src/components/audio-animation/index.tsx index 30111090..469734af 100644 --- a/src/components/audio-animation/index.tsx +++ b/src/components/audio-animation/index.tsx @@ -4,19 +4,40 @@ import './index.less'; interface AudioAnimationProps { width: number; height: number; + scaleFactor?: number; analyserData: { data: Uint8Array; analyser: any; }; } -const AudioAnimation: React.FC = (props) => { - const { width, height, analyserData } = props; +const AudioAnimation: React.FC = ({ + width, + height, + scaleFactor = 1.2, + analyserData +}) => { const canvasRef = React.useRef(null); const animationId = React.useRef(0); const isScaled = React.useRef(false); const oscillationOffset = React.useRef(0); const direction = React.useRef(1); + const maxBarCount = 128; + + const calculateJitter = ( + i: number, + timestamp: number, + baseHeight: number, + minJitter: number, + jitterAmplitude: number + ) => { + // + const jitterFactor = Math.sin(timestamp / 200 + i) * 0.5 + 0.5; + const jitter = + minJitter + + jitterFactor * (jitterAmplitude - minJitter) * (baseHeight / maxBarCount); + return jitter; + }; const startAudioVisualization = () => { if (!canvasRef.current || !analyserData.data?.length) return; @@ -33,42 +54,51 @@ const AudioAnimation: React.FC = (props) => { isScaled.current = true; } - const barWidth = 3; - const barSpacing = 2; - const centerX = HEIGHT / 2; + const barWidth = 4; + const barSpacing = 6; const centerLine = Math.floor(HEIGHT / 2); - const jitterAmplitude = 60; // 最大抖动幅度 - const minJitter = 15; // 最小抖动幅度 - const frameInterval = 2; - let frameCount = 0; + const jitterAmplitude = 40; + const minJitter = 10; + + let lastFrameTime = 0; - canvasCtx.fillStyle = '#0073EF'; + const gradient = canvasCtx.createLinearGradient(0, 0, 0, HEIGHT); + gradient.addColorStop(0, '#007BFF'); + gradient.addColorStop(1, '#0069DA'); + canvasCtx.fillStyle = gradient; - const draw = () => { - frameCount++; - if (frameCount % frameInterval !== 0) { + const draw = (timestamp: number) => { + const elapsed = timestamp - lastFrameTime; + if (elapsed < 16) { animationId.current = requestAnimationFrame(draw); return; } + lastFrameTime = timestamp; + analyserData.analyser?.current?.getByteFrequencyData(analyserData.data); canvasCtx.clearRect(0, 0, WIDTH, HEIGHT); - const barCount = analyserData.data.length; + const barCount = Math.min(maxBarCount, analyserData.data.length); const totalWidth = barCount * (barWidth + barSpacing) - barSpacing; - let x = centerX - totalWidth / 2 + oscillationOffset.current; - oscillationOffset.current += direction.current * 0.5; + let x = WIDTH / 2 - totalWidth / 2 + oscillationOffset.current; - if (oscillationOffset.current > 20 || oscillationOffset.current < -20) { + oscillationOffset.current += direction.current; + if (Math.abs(oscillationOffset.current) > 20) { direction.current *= -1; } for (let i = 0; i < barCount; i++) { - const baseHeight = Math.floor(analyserData.data[i] / 2); - const jitter = - minJitter + - Math.round((Math.random() - 0.5) * (jitterAmplitude - minJitter)); - const barHeight = baseHeight + jitter; + const baseHeight = Math.floor(analyserData.data[i] / 2) * scaleFactor; + + const jitter = calculateJitter( + i, + timestamp, + baseHeight, + minJitter, + jitterAmplitude + ); + const barHeight = baseHeight + Math.round(jitter); const topY = Math.round(centerLine - barHeight / 2); const bottomY = Math.round(centerLine + barHeight / 2); @@ -87,21 +117,29 @@ const AudioAnimation: React.FC = (props) => { animationId.current = requestAnimationFrame(draw); }; - draw(); + draw(performance.now()); }; useEffect(() => { - if (!analyserData.data?.length || !analyserData.analyser.current) { - canvasRef.current - ?.getContext('2d') - ?.clearRect(0, 0, width * 2, height * 2); + const clearCanvas = () => { + if (canvasRef.current) { + const ctx = canvasRef.current.getContext('2d'); + if (ctx) ctx.clearRect(0, 0, width * 2, height * 2); + } + }; + + if (!analyserData.data?.length || !analyserData.analyser?.current) { + clearCanvas(); cancelAnimationFrame(animationId.current); animationId.current = 0; return; } + startAudioVisualization(); + return () => { - if (animationId.current) cancelAnimationFrame(animationId.current); + cancelAnimationFrame(animationId.current); + clearCanvas(); }; }, [analyserData, width, height]); diff --git a/src/components/audio-player/index.less b/src/components/audio-player/index.less index f310c532..e82d1d65 100644 --- a/src/components/audio-player/index.less +++ b/src/components/audio-player/index.less @@ -75,6 +75,12 @@ .file-name { line-height: 20px; height: 20px; + display: flex; + align-items: center; + justify-content: center; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; } .ant-slider-horizontal { diff --git a/src/components/audio-player/index.tsx b/src/components/audio-player/index.tsx index 68be5d8d..459a3952 100644 --- a/src/components/audio-player/index.tsx +++ b/src/components/audio-player/index.tsx @@ -53,7 +53,7 @@ const AudioPlayer: React.FC = forwardRef((props, ref) => { }); const [playOn, setPlayOn] = React.useState(false); const [speakerOn, setSpeakerOn] = React.useState(false); - const [volume, setVolume] = React.useState(0.5); + const [volume, setVolume] = React.useState(1); const [speed, setSpeed] = React.useState(defaultSpeed); const timer = React.useRef(null); @@ -98,12 +98,12 @@ const AudioPlayer: React.FC = forwardRef((props, ref) => { }, []); const handlePlay = useCallback(() => { + setPlayOn(!playOn); if (playOn) { audioRef.current?.pause(); } else { audioRef.current?.play(); } - setPlayOn(!playOn); }, [playOn]); const handleFormatVolume = (val?: number) => { @@ -118,10 +118,12 @@ const AudioPlayer: React.FC = forwardRef((props, ref) => { setVolume(round(value, 2)); }, []); - const initPlayerConfig = useCallback(() => { - audioRef.current!.volume = volume; - audioRef.current!.playbackRate = speed; - }, []); + const initPlayerConfig = () => { + if (audioRef.current) { + audioRef.current!.volume = volume; + audioRef.current!.playbackRate = speed; + } + }; const handleLoadedMetadata = useCallback( (data: any) => { @@ -168,6 +170,10 @@ const AudioPlayer: React.FC = forwardRef((props, ref) => { }); }; + const handleOnLoad = (e: any) => { + console.log('onload', e); + }; + useEffect(() => { if (audioRef.current) { initPlayerConfig(); @@ -195,6 +201,7 @@ const AudioPlayer: React.FC = forwardRef((props, ref) => { = forwardRef((props, ref) => { {formatTime(audioState.duration)}
- {/* - } - > - - {speed ? `${speed}x` : '1x'} - - */} = forwardRef((props, ref) => {
- {/* - - {speakerOn && ( - - )} - */}