前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Js 音频律动

Js 音频律动

作者头像
白衣少年
发布2023-06-16 16:23:47
9670
发布2023-06-16 16:23:47
举报

这段时间在独立写音乐项目,在学习过程中接触到了JS的音频律动,于是找到了以下项目

js音频律动.png
js音频律动.png

以上是效果图

下面分享代码:

HTML结构

代码语言:javascript
复制
      Document

CSS样式

代码语言:javascript
复制
* {
  margin: 0;
  padding: 0;
}

body {
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  overflow: hidden;
  background: url(./test.jpg) center;
  background-size: cover;
  backdrop-filter: blur(50px) grayscale(50%);
}

.music-box {
  position: relative;
  width: 400px;
  height: 400px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.my-canvas {
  position: absolute;
  top: 0;
}

.my-music-btn {
  position: relative;
  width: 250px;
  height: 250px;
  background: url(./test.jpg);
  background-size: cover;
  border-radius: 50%;
  border: none;
  outline: none;
  animation: music-btn-anim 20s infinite linear;
}

.my-music-btn.rotate {
  animation-play-state: paused;
}

@keyframes music-btn-anim {
  from {
      transform: rotate(0deg);
  }

  to {
      transform: rotate(360deg);
  }
}

JS

{tabs-pane label="代码部分"}

代码语言:javascript
复制
// 音乐播放器
class MusicPlayer {
  constructor(data = {
      musicSrc: "./test.mp3",
      // musicImgSrc: "./music.jpg",
      effectColor: "#FFFFFF"
  }) {
      this._requestID = null;
      // 特效单体
      this._effectEntity = new Entity();
      this._effectEntity.addComp(new MusicBtnSingleComp({
          callback: () => {
              this._effectEntity.getComp("MusicBtnSingleComp").isRotate = !this._effectEntity.getComp("MusicBtnSingleComp").isRotate;
              !this._effectEntity.getComp("MusicSingleComp").isReady &&(this._effectEntity.getComp("MusicSingleComp").isReady = true);
              this._effectEntity.getComp("MusicSingleComp").isPlay = !this._effectEntity.getComp("MusicSingleComp").isPlay;

              if(!this._effectEntity.getComp("MusicSingleComp").isPlay) {
                  cancelAnimationFrame(this._requestID);
              } else {
                  this._requestID = requestAnimationFrame(this._renderFrame.bind(this));
              }
          }
      }));
      this._effectEntity.addComp(new MusicSingleComp({
          musicSrc: data.musicSrc
      }));
      this._effectEntity.addComp(new MusicEffectSingleComp({
          effectColor: data.effectColor
      }))
  }

  _renderFrame() {
      this._requestID = requestAnimationFrame(this._renderFrame.bind(this));

      this._effectEntity.getComp("MusicEffectSingleComp").byteFrequencyDate = this._effectEntity.getComp("MusicSingleComp").byteFrequencyDate;
  }
}

// 单体
class Entity {
  constructor() {
      this._compMap = new Map();
  }

  addComp(comp) {
      this._compMap.set(comp.name, comp);
  }

  getComp(compName) {
      return this._compMap.get(compName);
  }
}

// 音乐按钮
class MusicBtnSingleComp {
  constructor(data) {
      this.name = "MusicBtnSingleComp";
      this._isRotate = false;
      this._musicBtnDom = document.querySelector(".my-music-btn");
      this._musicBtnDom.addEventListener("click", data.callback);
  }

  set isRotate(value) {
      if (value) {
          this._musicBtnDom.classList.remove("rotate");
      } else {
          this._musicBtnDom.classList.add("rotate");
      }
      this._isRotate = value;
  }

  get isRotate() {
      return this._isRotate;
  }
}

// 音乐
class MusicSingleComp {
  constructor(data) {
      this.name = "MusicSingleComp";
      this._fftSize = 512;

      this._myAudioDom = document.createElement("audio");
      this._myAudioDom.src = data.musicSrc;
      this._myAudioDom.loop = true;

      this._isReady = false;
      this._isPlay = false;
      this._analyser = null;
      this._dataArray = [];
  }

  set isReady(value) {
      if (value) {
          const ctx = new window.AudioContext();
          this._analyser = ctx.createAnalyser();
          this._analyser.fftSize = this._fftSize;
          const source = ctx.createMediaElementSource(this._myAudioDom);
          source.connect(this._analyser);
          this._analyser.connect(ctx.destination);
          const bufferLength = this._analyser.frequencyBinCount;
          this._dataArray = new Uint8Array(bufferLength);
      }
      this._isReady = value;
  }

  get isReady() {
      return this._isReady;
  }

  set isPlay(value) {
      if (value) {
          this._myAudioDom.play();
      } else {
          this._myAudioDom.pause();
      }
      this._isPlay = value;
  }

  get isPlay() {
      return this._isPlay;
  }

  get byteFrequencyDate() {
      this._analyser.getByteFrequencyData(this._dataArray);
      return this._dataArray.slice(0, 120);
  }
}

// 音乐特效
class MusicEffectSingleComp {
  constructor(data) {
      this.name = "MusicEffectSingleComp";
      this._effectColor = data.effectColor;
      this._canvasDom = document.querySelector(".my-canvas");
      this._canvasDom.width = 400;
      this._canvasDom.height = 400;
      this._ctx = this._canvasDom.getContext("2d");
      this._byteFrequencyData;
      this._randomData = Uint8Array.from(new Uint8Array(120), (v,k) => k);
      this._randomData.sort(() => Math.random() - 0.5);
      this.byteFrequencyDate = new Uint8Array(120).fill(0);
  }

  set byteFrequencyDate(value) {
      this._byteFrequencyData = value;
      const bData = [];
      this._randomData.forEach(value => {
          bData.push(this._byteFrequencyData[value]);
      })

      const angle = Math.PI * 2 / bData.length;
      this._ctx.clearRect(0, 0, this._canvasDom.width, this._canvasDom.height);
      this._ctx.fillStyle = this._effectColor;
      this._ctx.save();
      this._ctx.translate(this._canvasDom.width / 2, this._canvasDom.height / 2);
      bData.forEach((value, index) => {
          this._ctx.save();
          this._ctx.rotate(angle * index);
          this._ctx.beginPath();
          const h = value / 256 * 60;
          this._ctx.roundRect(-4, 140, 4, (h < 4) ? 4 : h, 4);
          // 若上行的 roundRect 存在兼容性问题可以更换为下面注释的代码
          // this._ctx.fillRect(-4, 140,  4, (h < 4) ? 4 : h);
          this._ctx.fill();
          this._ctx.restore();
      });
      this._ctx.restore();
  }
}

new MusicPlayer();

{/tabs-pane} {tabs-pane label="代码解释"} 这是一个使用 JavaScript 原生 API 实现的音乐播放器,包含音乐按钮、音乐、音乐特效三个部分。其中:

  • MusicPlayer:音乐播放器类,通过构造函数创建音乐播放器实例,同时包含特效单体(EffectEntity)。
  • Entity:单体类,通过 addComp 和 getComp 方法向特效单体中添加和获取组件。
  • MusicBtnSingleComp:音乐按钮组件,包含事件监听、旋转特效等。
  • MusicSingleComp:音乐组件,负责音乐的加载、配置播放参数和获取音频频谱数据。
  • MusicEffectSingleComp:音乐特效组件,通过获取音频频谱数据,实现了可视化的音乐特效。

其中 MusicEffectSingleComp 中的 _ctx.roundRect 方法,可能是用户自定义的实现;如果有兼容性问题,可以更换到代码注释处的相应代码。 {/tabs-pane}

使用时仅需将图片和音频放于项目根目录并重命名为test.jpgtest.mp3即可

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 这段时间在独立写音乐项目,在学习过程中接触到了JS的音频律动,于是找到了以下项目
  • 以上是效果图
    • HTML结构
      • CSS样式
        • JS
        • 使用时仅需将图片和音频放于项目根目录并重命名为test.jpg 和 test.mp3即可
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档