Skip to content

v-audiopectrum 指令

介绍

v-audiopectrum 是一个音频频谱可视化指令,能够将音频元素的波形和频率数据实时渲染为动态图形。支持 ​三种内置可视化模式 和 ​完全自定义渲染,适用于音乐播放器、语音分析、音效可视化等场景。

核心特性

  • 实时音频数据分析(时域/频域)
  • 三种预制可视化模式:柱状图/波形图/圆形谱
  • 响应式布局自动适配容器尺寸
  • 自定义颜色方案和渲染逻辑
  • 完善的资源清理机制

基础用法

请记得打开声音,没有声音是不会显示的哦!

音频统一为 《trun the lights back on》

查看代码
vue
<!-- AudioBasicDemo.vue -->
<template>
    <div class="demo-container">
      <audio 
        v-audiopectrum
        controls
        src="../.../../../../public/directives/audiopectrum/trun the lights back on.mp3"
      ></audio>
    </div>
  </template>
  
  <script setup lang="ts">
  </script>
  
  <style scoped>
  .demo-container {
    padding: 20px;
    border: 1px solid #eee;
    border-radius: 8px;
    margin: 20px 0;
  }
  </style>

进阶用法

圆形特效参数

查看代码
vue
<template>
    <div class="demo-container">
        <audio v-audiopectrum="options" controls :src="audioUrl"></audio>

        <div class="controls">
            <div class="control-group">
                <label>显示模式:
                    <select v-model="options.mode">
                        <option value="bars">柱状图</option>
                        <option value="waveform">波形图</option>
                        <option value="circular">圆形模式</option>
                    </select>
                </label>
            </div>

            <div class="control-group" v-if="options.mode === 'circular'">
                <h4>圆形特效参数</h4>
                <label>脉冲间隔:
                    <input type="range" v-model.number="options.circlePulseInterval" min="50" max="500">
                    {{ options.circlePulseInterval }}ms
                </label>

                <label>生命周期:
                    <input type="range" v-model.number="options.circleLifetime" min="500" max="2000">
                    {{ options.circleLifetime }}ms
                </label>

                <label>最大数量:
                    <input type="range" v-model.number="options.circleMaxCount" min="1" max="10">
                    {{ options.circleMaxCount }}
                </label>
            </div>

            <div class="color-picker">
                <label>起始颜色:
                    <input type="color" v-model="options.colors.start">
                </label>
                <label>结束颜色:
                    <input type="color" v-model="options.colors.end">
                </label>
            </div>
        </div>
    </div>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue'

const audioUrl = ref('../../../public/directives/audiopectrum/trun the lights back on.mp3')

const options = ref({
    mode: 'circular',
    fftSize: 1024,
    colors: {
        start: '#00ff88',
        end: '#ff0077'
    }
})


</script>

<style scoped>
.demo-container {
    padding: 20px;
    border: 1px solid #eee;
    border-radius: 8px;
    margin: 20px 0;
}

.controls {
    margin-top: 15px;
    padding: 15px;
    background: #f8f8f8;
    border-radius: 6px;
}

.control-group {
    margin-bottom: 15px;
}

label {
    display: block;
    margin: 8px 0;
}

input[type="range"] {
    vertical-align: middle;
}

.color-picker input[type="color"] {
    width: 40px;
    height: 30px;
    margin-right: 10px;
}
</style>

自定义渲染指南

查看代码
vue
<template>
    <div class="demo-container">
      <audio 
        v-audiopectrum="{ 
          mode: 'custom', 
          customDraw: particleDraw,
          fftSize: 256
        }"
        controls
        :src="audioUrl"
      ></audio>
    </div>
  </template>
  
  <script setup lang="ts">
  import { ref } from 'vue'
  
  const audioUrl = ref('../../../public/directives/audiopectrum/trun the lights back on.mp3')
  
  // 自定义粒子绘制函数
  const particleDraw = ({ ctx, dataArray, canvas, analyzer }) => {
    ctx.fillStyle = 'rgba(0, 0, 0, 0.1)'
    ctx.fillRect(0, 0, canvas.width, canvas.height)
  
    const centerX = canvas.width / 2
    const centerY = canvas.height / 2
    const maxDistance = Math.min(centerX, centerY) * 0.8
  
    // 获取频率数据
    const freqData = new Uint8Array(analyzer.frequencyBinCount)
    analyzer.getByteFrequencyData(freqData)
  
    // 绘制粒子
    const particleCount = 200
    for (let i = 0; i < particleCount; i++) {
      const angle = (i / particleCount) * Math.PI * 2
      const freqIndex = Math.floor((i / particleCount) * freqData.length)
      const amplitude = freqData[freqIndex] / 255
      
      // 计算位置
      const distance = maxDistance * amplitude
      const x = centerX + Math.cos(angle) * distance
      const y = centerY + Math.sin(angle) * distance
      
      // 绘制粒子
      ctx.beginPath()
      ctx.arc(x, y, 2 + amplitude * 4, 0, Math.PI * 2)
      ctx.fillStyle = `hsla(${(freqIndex / freqData.length) * 360}, 70%, 50%, ${0.5 + amplitude * 0.5})`
      ctx.fill()
    }
  
    // 绘制中心圆
    ctx.beginPath()
    ctx.arc(centerX, centerY, 10, 0, Math.PI * 2)
    ctx.fillStyle = '#fff'
    ctx.fill()
  }
  </script>
  
  <style scoped>
  .demo-container {
    padding: 20px;
    border: 1px solid #eee;
    border-radius: 8px;
    margin: 20px 0;
  }
  </style>

接口说明

通过 customDraw 参数传入自定义渲染函数,接收以下参数对象:

typescript
interface DrawContext {
  ctx: CanvasRenderingContext2D // 画布绘制上下文
  dataArray: Uint8Array // 当前帧音频数据(根据模式不同含义不同)
  canvas: HTMLCanvasElement // 画布DOM元素
  analyzer: AnalyserNode // Web Audio分析器实例
  colors: {
    // 颜色配置
    start: string
    end: string
  }
}

API

属性名说明类型是否必选默认值
mode
可视化模式选择
bars | waveform | circular | custom(自定义)
bars
fftSize
FFT转换窗口大小(需为2的幂次方)
number
256
colors
渐变颜色配置(起始色/结束色)
{ start: string, end: string }
{ start: "#00ff00", end: "#ff0000" }
customDraw
自定义渲染函数(需配合 mode: "custom" 使用)
function
null

注意事项

WARNING

  • ​音频上下文限制

需要用户交互后初始化(点击事件后生效) 单页面最多同时存在 ​4个 实例

  • ​性能优化

移动端建议设置 fftSize: 128 复杂场景使用 requestIdleCallback 优化

  • ​内存管理
vue
<audio v-if="shouldShow" v-audiopectrum />
<!-- 正确 -->
<audio v-show="shouldShow" v-audiopectrum />
<!-- 错误 -->