chore: slider to change width and height in image

This commit is contained in:
jialin
2024-12-02 08:20:30 +08:00
parent 412c6dadca
commit ec2cd29096
24 changed files with 500 additions and 283 deletions
@@ -6,5 +6,6 @@
canvas {
display: block;
image-rendering: crisp-edges;
}
}
+66 -28
View File
@@ -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]);