웹 프런트엔드 오디오 시각화, 형식화된 배열, 16진수 임의 색상, devicePixelRatio, AudioContext, createMediaElementSource, createAnalyser


머리말

비공개 채팅에서 무료로 소스 코드 받기: MJ682517
1. 오디오가 정확해야 합니다. 네트워크 오디오를 재생할 때 소리가 나지 않습니다. 로컬 오디오만
재생할 수 있습니다. 2. Kugou에서 다운로드한 kgma 형식 오디오는 지원되지 않습니다.
3. I
4. 코드 분석은 모두 코드에 들어있습니다.


렌더링

512
web_audioVisualization_512


1024
web_audioVisualization_1024


HTML

<body class="bc_333">
  <div class="box d_f fd_c jc_c">
      <canvas></canvas>
      <hr class="w_100_ bc_dcdcdc h_1 b_n" />
      <audio class="w_86_ m_a" controls loop src="./1.mp3"></audio>
      <div class="m_t_10">
          <div id="idIntervalBtn" class="w_50_ m_lr_a d_f jc_sb"></div>
          <div id="idClothCoverBtn" class="w_50_ m_lr_a d_f jc_sb m_t_10"></div>
      </div>
  </div>
</body>

자바스크립트

// 全局变量
let audioEle = document.querySelector('audio'),
    cvs = document.querySelector('canvas'),
    ctx = cvs.getContext('2d'),
    // 控制初始化
    isInit = false,
    dataArray = undefined,
    analyser = undefined,
    animation = undefined,
    // 按钮操作
    interval = 2,
    // 条形粗细
    clothCover = 1024;

// 初始化页面
initPage();
function initPage() {
    
    
    let intervalEl = '',
        clothCoverEl = '';

    for (let i = 2; i < 9; i++) intervalEl += `<button class="c_p w_68 radius_6 fw_700 ${
      
      interval === i ? 'color_blue' : ''}" οnclick="handleInterval(event, ${
      
      i})">${
      
      i}</button>`;

    for (let i = 9; i < 12; i++) {
    
    
        let val = 2 ** i;

        clothCoverEl += `<button class="c_p w_68 radius_6 fw_700 ${
      
      clothCover === val ? 'color_blue' : ''}" οnclick="handleClothCover(${
      
      val})">${
      
      val}</button>`;
    }

    idIntervalBtn.innerHTML = intervalEl;
    idClothCoverBtn.innerHTML = clothCoverEl;
}

// 初花canvas的尺寸
initCvs();
function initCvs() {
    
    
    cvs.width = window.innerWidth * devicePixelRatio;
    cvs.height = (window.innerHeight / 1.5) * devicePixelRatio;
}

// 播放
audioEle.onplay = handlePlay;

function handlePlay() {
    
    
    if (isInit) {
    
    
        analyser.fftSize = clothCover;

        return draw();
    }

    // 初始化
    // 创建音频上下文
    let audCtx = new AudioContext(),
        // 创建音频源节点
        source = audCtx.createMediaElementSource(audioEle);

    // 分析器节点
    analyser = audCtx.createAnalyser();
    // 设置变换窗口大小
    // 默认值为2048,必须是2的n次幂
    // 数值越大,则得到的值与音频频率更接近
    analyser.fftSize = clothCover;
    // 创建数组,用于接收分析器节点的分析数据
    // 类型化数组
    // 每一项代表一个字节
    // 表示数组里面的每一项是一个无符号的8位整数
    // new Uint8Array(512 / 2);
    // frequencyBinCount属性会自动把值除以2
    dataArray = new Uint8Array(analyser.frequencyBinCount);
    // 音频源与分析器连接
    source.connect(analyser);
    // 分析器连接到输出设备(输出声音)
    analyser.connect(audCtx.destination);
    isInit = true;
    draw();
}

// 暂停
audioEle.onpause = function () {
    
    
    cancelAnimationFrame(animation);
}

// 把分析出的波形绘制到canvas
function draw() {
    
    
    // 类似递归(循环绘制)
    animation = requestAnimationFrame(draw);
    let {
    
     width, height } = cvs,
        len = 0,
        barWidth = 0,
        gradient = undefined;

    // 清空画布
    ctx.clearRect(0, 0, width, height);
    if (!isInit) return console.log('未初始化!');
    // 让分析器节点分析出数据到数组中
    analyser.getByteFrequencyData(dataArray);

    len = dataArray.length / 2.5;
    barWidth = width / len / 2;
    // ctx.fillStyle = '#78c5f7';
    // 创建线性渐变对象(横向渐变)
    gradient = ctx.createLinearGradient(0, 0, width, 0);
    // 添加渐变色段
    gradient.addColorStop(0, hexadecimalColor());
    gradient.addColorStop(0.5, hexadecimalColor());
    gradient.addColorStop(1, hexadecimalColor());

    ctx.fillStyle = gradient;

    for (let i = 0; i < len; i++) {
    
    
        let data = dataArray[i],
            barHeight = data / 255 * height,
            // / 2 波形图取一半
            x1 = i * barWidth + width / 2,
            // 波形图另一半
            x2 = width / 2 - (i + 1) * barWidth,
            // x1和x2为对称图形
            // 只是x轴坐标不同
            // y轴一样
            y = height - barHeight;

        // - 2 让每个条之间有缝隙

        // 右半边
        ctx.fillRect(x1, y, barWidth - interval, barHeight);
        //  左半边
        ctx.fillRect(x2, y, barWidth - interval, barHeight);
    }
}

// 随机十六进制颜色
function hexadecimalColor() {
    
    
    let str = '0123456789abcdef',
        len = str.length,
        shuffleArray = undefined,
        arr = [],
        color = '#';

    str = str.split('');
    shuffleArray = (arr) => arr.sort(() => Math.random() - 0.5);
    arr = shuffleArray(str);
    str = arr.toString();
    str = str.replace(/,/g, '');

    for (let i = 0; i < 6; i++) {
    
    
        let j = Math.floor(Math.random() * len);
        color += str[j];
    }

    return color;
}

// 间隔设置
function handleInterval(event, value) {
    
    
    let el = document.querySelector('#idIntervalBtn').childNodes;

    for (let i = 0; i < el.length; i++) {
    
    
        const val = el[i].textContent;
        if (value == val) {
    
    
            event.target.classList.add('color_blue');
        } else {
    
    
            el[i].classList.remove('color_blue');
        }
    }
    interval = value;
}

// 条形粗细
function handleClothCover(value) {
    
    
    let el = document.querySelector('#idClothCoverBtn').childNodes;

    for (let i = 0; i < el.length; i++) {
    
    
        const val = el[i].textContent;
        if (value == val) {
    
    
            event.target.classList.add('color_blue');
        } else {
    
    
            el[i].classList.remove('color_blue');
        }
    }

    clothCover = value;

    handlePlay();
}

CSS

.bc_333 {
    
    
    background-color: #333333;
}

.bc_dcdcdc {
    
    
    background-color: #dcdcdc;
}

.h_1 {
    
    
    height: 1px;
}

.b_n {
    
    
    border: none;
}

.c_p {
    
    
    cursor: pointer;
}

.w_50_ {
    
    
    width: 50%;
}

.w_86_ {
    
    
    width: 86%;
}

.w_100_ {
    
    
    width: 100%;
}

.d_f {
    
    
    display: flex;
}

.fd_c {
    
    
    flex-direction: column;
}

.jc_sb {
    
    
    justify-content: space-between;
}

.jc_c {
    
    
    justify-content: center;
}

.m_a {
    
    
    margin: auto;
}

.m_lr_a {
    
    
    margin-left: auto;
    margin-right: auto;
}

.m_t_10 {
    
    
    margin-top: 10px;
}

.w_68 {
    
    
    width: 68px;
}

.radius_4 {
    
    
    border-radius: 4;
}

.radius_6 {
    
    
    border-radius: 6;
}

.color_blue {
    
    
    color: #0000ff;
}

.fw_700 {
    
    
    font-weight: 700;
}

추천

출처blog.csdn.net/weixin_51157081/article/details/132867718