音频可视化

第一步创建一个音频上下文(也就是一个管理音频的构造函数)
 

audioContext = new (window.AudioContext || window.webkitAudioContext)();

第二步通过音频上下文创建出处理音频数据的“人”

analyser = audioContext.createAnalyser();

第三步创建一个读取音频数据的“人”
 

const source = audioContext.createMediaElementSource(audio.value);

第四步读取音频数据的 “人” 将数据交给处理数据的“人”
 

source.connect(analyser);

第五步 处理的数据的“人”将处理好的音频数据进行输出外放
 

analyser.connect(audioContext.destination);

 第六步就是Canvas绘图
 

const drawVisualizer = () => {
  if (!visualizer.value || !analyser) return;

  const canvas = visualizer.value;
  const ctx = canvas.getContext("2d");

  // 设置canvas尺寸
  canvas.width = canvas.offsetWidth;
  canvas.height = canvas.offsetHeight;

  const draw = () => {
    animationId = requestAnimationFrame(draw);

    // 获取解析出上下文的数据并存在dataArray中
    analyser.getByteFrequencyData(dataArray);

    // 初始化清空画布
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // 设置柱状图参数
    const barCount = 160;
    const barSpacing = 2;
    const barWidth = 2;
    const baseHeight = 3;

    // 计算总宽度并居中
    const totalWidth = (barWidth + barSpacing) * barCount;
    const startX = (canvas.width - totalWidth) / 2;

    // 绘制音频柱状图
    for (let i = 0; i < barCount; i++) {
      // 平滑数据
      const startIndex = Math.floor((i * dataArray.length) / barCount);
      const endIndex = Math.floor(((i + 1) * dataArray.length) / barCount);
      let sum = 0;
      for (let j = startIndex; j < endIndex; j++) {
        sum += dataArray[j];
      }
      const value = sum / (endIndex - startIndex);

      // 计算高度,添加安全检查
      const percent = Math.min(Math.max(value / 255, 0), 1); // 确保百分比在 0-1 之间
      const height = Math.max(baseHeight, canvas.height * 0.85 * percent);

      // 计算位置,添加安全检查
      const x = Math.max(0, startX + i * (barWidth + barSpacing));
      const y = Math.min(canvas.height - height, canvas.height); // 确保 y 不会小于 0

      // 绘制发光效果
      ctx.shadowBlur = 12;
      ctx.shadowColor = "#00ffea";

      try {
        // 创建渐变时添加错误处理
        if (isFinite(y) && y >= 0 && y <= canvas.height) {
          const gradient = ctx.createLinearGradient(x, y, x, canvas.height);
          gradient.addColorStop(0, "#00ffea");
          gradient.addColorStop(1, "#00ffea33");
          ctx.fillStyle = gradient;
        } else {
          // 如果值无效,使用固定颜色
          ctx.fillStyle = "#00ffea";
        }

        // 绘制主体
        ctx.fillRect(x, y, barWidth, height);

        // 绘制顶部小圆点
        ctx.beginPath();
        ctx.arc(x + barWidth / 2, y, barWidth / 2, 0, Math.PI * 2);
        ctx.fillStyle = "#00ffea";
        ctx.fill();

        // 添加星星效果(随机)
        if (Math.random() < 0.01) {
          ctx.beginPath();
          const starX = Math.random() * canvas.width;
          const starY = Math.random() * canvas.height;
          ctx.arc(starX, starY, 0.8, 0, Math.PI * 2);
          ctx.fillStyle = "#00ffea44";
          ctx.fill();
        }
      } catch (error) {
        console.error("绘制错误:", error);
        continue; // 跳过当前柱子的绘制
      }
    }
  };

  draw();
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值