跳躍音波

https://juejin.cn/post/6844903604013252621#heading-11

<template>
  <div class="page">
    <canvas id="waveCanvas" height="250"></canvas>
    <audio src="./assets/1.mp3" controls id="audio">親 您的瀏覽器不支持html5的audio標(biāo)簽</audio>
    <button @click="play">play</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, onMounted } from 'vue'

export default defineComponent({
  name: 'Home',
  components: {
  
  },
  data() {
    return {
    }
  },
  setup() {
    let oAudio: any = null;
    // 創(chuàng)建音頻上下文對象
    let audioSrc: any = null;
    let analyser: any = null;
    let voiceHeight: any = null;

    function play() {
      init()
      animation()
    }

    function initCanvas() {
      oAudio = document.getElementById('audio')

      let canvas: any = document.getElementById('waveCanvas');
      let ctx = canvas.getContext('2d');
      //設(shè)置canvas寬高
      let width = canvas.offsetWidth;
      // let height = canvas.offsetHeight;
      let height = 250;
      canvas.width = width;
      canvas.height = height;
      let baseY = height - 50;
      //畫出矩形方框,this.baseY是方框高度相對于窗口高度的基準(zhǔn)線
      ctx.beginPath();
      ctx.fillStyle = 'rgba(31,165,23,0.6)';
      ctx.moveTo(0, baseY);
      ctx.lineTo(width, baseY);
      ctx.lineTo(width, height);
      ctx.lineTo(0, height);
      ctx.fill();
    }

    function init () {
      oAudio.play()
      if (!audioSrc) {
        let oCtx = new window.AudioContext();
        // 創(chuàng)建媒體源,除了audio本身可以獲取,也可以通過oCtx對象提供的api進行媒體源操作
        audioSrc = oCtx.createMediaElementSource(oAudio);
        // 創(chuàng)建分析機 
        analyser = oCtx.createAnalyser();
        // 媒體源與分析機連接
        audioSrc.connect(analyser);
        // 輸出的目標(biāo):將分析機分析出來的處理結(jié)果與目標(biāo)點(耳機/揚聲器)連接
        analyser.connect(oCtx.destination);
        voiceHeight = new Uint8Array(analyser.frequencyBinCount);
      }
    }

    function draw(waveArr1: any, waveArr2: any) {
      analyser.getByteFrequencyData(voiceHeight);
      let canvas: any = document.getElementById('waveCanvas');
      let ctx = canvas.getContext('2d');
      //設(shè)置canvas寬高
      let width = canvas.offsetWidth;
      // let height = canvas.offsetHeight;
      let height = 250;
      let baseY = height - 50;
      canvas.width = width;
      canvas.height = height;

      let waveWidth = Math.ceil(width / (waveArr1.length - 3));
      let waveWidth2 =  Math.ceil(width / (waveArr2.length - 3));

      //畫出矩形方框,this.baseY是方框高度相對于窗口高度的基準(zhǔn)線
      ctx.beginPath();
      ctx.fillStyle = 'rgba(31,165,23,0.2)';
      ctx.moveTo(-waveWidth * 2, baseY - waveArr1[0]);

      for(let i = 1; i < waveArr1.length - 2; i ++) {
        let p0 = {x: (i - 2) * waveWidth, y:waveArr1[i - 1]};
        let p1 = {x: (i - 1) * waveWidth, y:waveArr1[i]};
        let p2 = {x: (i) * waveWidth, y:waveArr1[i + 1]};
        let p3 = {x: (i + 1) * waveWidth, y:waveArr1[i + 2]};

        for(let j = 0; j < 100; j ++) {
            let t = j * (1.0 / 100);
            let tt = t * t;
            let ttt = tt * t;
            let CGPoint: any ={};
            CGPoint.x = 0.5 * (2*p1.x+(p2.x-p0.x)*t + (2*p0.x-5*p1.x+4*p2.x-p3.x)*tt + (3*p1.x-p0.x-3*p2.x+p3.x)*ttt);
            CGPoint.y = 0.5 * (2*p1.y+(p2.y-p0.y)*t + (2*p0.y-5*p1.y+4*p2.y-p3.y)*tt + (3*p1.y-p0.y-3*p2.y+p3.y)*ttt);
            ctx.lineTo(CGPoint.x, baseY - CGPoint.y);
        }
        ctx.lineTo(p2.x, baseY - p2.y);
      }

      ctx.lineTo((waveArr1.length) * waveWidth, baseY - waveArr1[waveArr1.length - 1]);
      ctx.lineTo(width + waveWidth * 2, baseY);
      ctx.lineTo(width + waveWidth * 2, height);
      ctx.lineTo(-2 * waveWidth, height);
      ctx.fill();

      ctx.beginPath();
      ctx.fillStyle = 'rgba(31,165,23,0.5)';
      ctx.moveTo(-waveWidth2 * 2, baseY - waveArr2[0]);

      for(let i = 1; i < waveArr2.length - 2; i ++) {
        let p0 = {x: (i - 2) * waveWidth2, y:waveArr2[i - 1]};
        let p1 = {x: (i - 1) * waveWidth2, y:waveArr2[i]};
        let p2 = {x: (i) * waveWidth2, y:waveArr2[i + 1]};
        let p3 = {x: (i + 1) * waveWidth2, y:waveArr2[i + 2]};

        for(let j = 0; j < 100; j ++) {
            let t = j * (1.0 / 100);
            let tt = t * t;
            let ttt = tt * t;
            let CGPoint: any ={};
            CGPoint.x = 0.5 * (2*p1.x+(p2.x-p0.x)*t + (2*p0.x-5*p1.x+4*p2.x-p3.x)*tt + (3*p1.x-p0.x-3*p2.x+p3.x)*ttt);
            CGPoint.y = 0.5 * (2*p1.y+(p2.y-p0.y)*t + (2*p0.y-5*p1.y+4*p2.y-p3.y)*tt + (3*p1.y-p0.y-3*p2.y+p3.y)*ttt);
            ctx.lineTo(CGPoint.x, baseY - CGPoint.y);
        }
        ctx.lineTo(p2.x, baseY - p2.y);
      }

      ctx.lineTo((waveArr2.length) * waveWidth2, baseY - waveArr2[waveArr2.length - 1]);
      ctx.lineTo(width + waveWidth2 * 2, baseY);
      ctx.lineTo(width + waveWidth2 * 2, height);
      ctx.lineTo(-2 * waveWidth2, height);
      ctx.fill();

    }

    function animation() {
      analyser.getByteFrequencyData(voiceHeight);
      let waveArr1 = [],
      waveArr2 = [],
      waveTemp: any[] = [],
      leftTemp: any[] = [],
      rightTemp: any[] = [],
      waveStep = 50,
      leftStep = 70, 
      rightStep = 90;
      voiceHeight.map((data: any) => {
          if(waveStep == 50 && waveTemp.length < 9) {
              waveTemp.push(data / 2.6);
              waveStep = 0;
          }else{
              waveStep ++;
          }
          if(leftStep == 0 && leftTemp.length < 5) {
              leftTemp.unshift(Math.floor(data / 4.8));
              leftStep = 70;
          }else {
              leftStep --;
          }
          if(rightStep == 0 && rightTemp.length < 5) {
              rightTemp.push(Math.floor(data / 4.8));
              rightStep = 90;
          }else {
              rightStep --;
          }
      });
      waveArr1 = leftTemp.concat(waveTemp).concat(rightTemp);
      waveArr2 = leftTemp.concat(rightTemp);
      waveArr2.map((data, k) => {
          waveArr2[k] = data * 1.8;
      });
      draw(waveArr1, waveArr2)
      window.requestAnimationFrame(animation);
    }

    onMounted(() => {
      initCanvas()
    })

    return {
      play
    }
  },
})
</script>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容