Components

Audio Visualization

<template>
  <div class="bg-black w-[1500px] h-[500px]">
    <canvas ref="canvasRef" width="1500" height="500"></canvas>
  </div>
  // 随便搞个音频
  <audio
    ref="audioRef"
    src="src/assets/music.mp3"
    controls
    @play="playMusic"
  ></audio>
</template>

<script lang="ts" setup>
const canvasRef = ref<HTMLCanvasElement>();
const audioRef = ref();
let ctx: CanvasRenderingContext2D;
let isInit = false;
let dataArray: Uint8Array, analyser: AnalyserNode;

const playMusic = () => {
  if (isInit) {
    return;
  }
  const audCtx = new AudioContext(); // 创建音频上下文
  const source = audCtx.createMediaElementSource(audioRef.value); // 创建音频源
  analyser = audCtx.createAnalyser(); // 创建分析器
  analyser.fftSize = 512; // 设置分析器的大小
  dataArray = new Uint8Array(analyser.frequencyBinCount); // 创建一个数组,用于存放分析器的数据
  source.connect(analyser); // 将音频源连接到分析器
  analyser.connect(audCtx.destination); // 将分析器连接到音频上下文的目的地
  isInit = true;
};

const draw = () => {
  requestAnimationFrame(draw);
  const { width, height } = canvasRef.value!;
  ctx.clearRect(0, 0, width, height);
  if (!isInit) {
    return;
  }
  analyser.getByteFrequencyData(dataArray); // 将分析器的数据复制到数组中
  const len = dataArray.length / 2.5;
  const barWidth = width / len / 2;
  ctx.fillStyle = "#78c5f7";

  for (let i = 0; i < len; i++) {
    const data = dataArray[i];
    const barHeight = (data / 255) * height;
    const x1 = i * barWidth + width / 2;
    const x2 = width / 2 - (i + 1) * barWidth;
    const y = height - barHeight;
    ctx.fillRect(x1, y, barWidth - 2, height);
    ctx.fillRect(x2, y, barWidth - 2, height);
  }
};

onMounted(() => {
  ctx = canvasRef.value!.getContext("2d") as CanvasRenderingContext2D;
  draw();
});
</script>