chore: slider to change width and height in image
This commit is contained in:
@@ -6,5 +6,6 @@
|
||||
|
||||
canvas {
|
||||
display: block;
|
||||
image-rendering: crisp-edges;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,19 +4,40 @@ import './index.less';
|
||||
interface AudioAnimationProps {
|
||||
width: number;
|
||||
height: number;
|
||||
scaleFactor?: number;
|
||||
analyserData: {
|
||||
data: Uint8Array;
|
||||
analyser: any;
|
||||
};
|
||||
}
|
||||
|
||||
const AudioAnimation: React.FC<AudioAnimationProps> = (props) => {
|
||||
const { width, height, analyserData } = props;
|
||||
const AudioAnimation: React.FC<AudioAnimationProps> = ({
|
||||
width,
|
||||
height,
|
||||
scaleFactor = 1.2,
|
||||
analyserData
|
||||
}) => {
|
||||
const canvasRef = React.useRef<HTMLCanvasElement>(null);
|
||||
const animationId = React.useRef<number>(0);
|
||||
const isScaled = React.useRef<boolean>(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<AudioAnimationProps> = (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;
|
||||
|
||||
canvasCtx.fillStyle = '#0073EF';
|
||||
let lastFrameTime = 0;
|
||||
|
||||
const draw = () => {
|
||||
frameCount++;
|
||||
if (frameCount % frameInterval !== 0) {
|
||||
const gradient = canvasCtx.createLinearGradient(0, 0, 0, HEIGHT);
|
||||
gradient.addColorStop(0, '#007BFF');
|
||||
gradient.addColorStop(1, '#0069DA');
|
||||
canvasCtx.fillStyle = gradient;
|
||||
|
||||
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<AudioAnimationProps> = (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]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user