前言
在数字化时代,我们正在见证音乐和视觉艺术的深度融合。流行音乐图像化,即将音乐视觉化,成为了一种独特的艺术表达方式。它跨越了时间和空间,用视觉元素诠释音乐的情感和氛围,为听众带来全新的感官体验。
今天分享的是一个基于WebAudio、canvas、three.js实现的简易音乐播放器。由于篇幅过长,所以分为三篇文章进行讲解。该篇文章主要叙述通过trree.js实现3d音频可视化。
音乐播放功能实现请查看 基于canvas和three.js实现音频可视化一
canvas音频可视化效果请查看 基于canvas和three.js实现音频可视化二
先来看下一最终效果:
实现简介
本篇文章主要讲述使用three.js实现音频可视化。在此之前你需要掌握three.js基本使用方法。
功能实现分为以下几个步骤:
- 添加html标签,设置css样式
- 初始化场景、相机、渲染器,添加光源、实体。
- 根据音频每一帧数据实现实体动画
1. 添加html标签,设置宽高和css样式
html代码
<van-swipe-item>
<div class="threeContainer" ref="threeContainerRef"></div>
</van-swipe-item>
css样式
.threeContainer {
width: 100%;
height: 400px;
}
2. 初始化场景、相机、渲染器,添加光源、添加实体
新建threeMusic.js文件,引入three.js,初始化变量,新建场景、相机、渲染器,添加光源、添加实体,具体实现请看代码
import * as THREE from './js/three.js'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
let audioContextOrigin, requestID = null;
const LENGTH = 240
const STEP = 12; // 定义步长
const CUBE_NUM = Math.ceil(LENGTH / STEP); // 要创建的长方体个数
const width = window.innerWidth;
const height = 500;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 3000);
const renderer = new THREE.WebGLRenderer();
/**
* 频谱立方体分组
*/
const cubes = new THREE.Group();
function create(parentDom) {
// 设置灯光效果
const pointLight = new THREE.PointLight( 0xffffff );
pointLight.position.set(0, 300, 40);
scene.add(pointLight);
const pointLight1 = new THREE.PointLight( 0xffffff );
pointLight1.position.set(-30, 30, 40);
scene.add(pointLight1);
const pointLight2 = new THREE.PointLight( 0xffffff );
pointLight2.position.set(30, -30, 40);
scene.add(pointLight2);
const pointLight3 = new THREE.PointLight( 0xffffff );
pointLight2.position.set(-30, -30, 40);
scene.add(pointLight2);
// 设置相机视角位置
camera.position.set(0,100, 1000);
camera.lookAt(scene.position);
renderer.setSize(width, height);
parentDom.appendChild(renderer.domElement)
renderer.render(scene, camera)
// 创建初始长方体
for (let i = 0; i < CUBE_NUM; i ++ ) {
// 长方体初始大小长、宽、高都为10
const geometry = new THREE.BoxGeometry( 10, 10, 10 );
const material = new THREE.MeshBasicMaterial({color: 'yellowgreen'});
const cube = new THREE.Mesh( geometry, material );
// 长方体间距为10,坐标位置为长方体宽度10加上间距10 乘以索引
cube.translateX((10 + 10) * i);
cube.translateY(1);
cubes.add(cube);
}
// 整体向x轴负半轴移动一半距离
cubes.translateX(- (10 +10) * CUBE_NUM / 2);
scene.add(cubes);
}
const controls = new OrbitControls(camera, renderer.domElement);
3、根据音频每一帧数据实现实体动画
新增长方体大小控制方法,新增控制动画类
function render() {
// 获取音频数据
let frequencyData = audioContextOrigin.byteFrequencyDate
// 截取长度为240
frequencyData = frequencyData.slice(0, LENGTH)
const averageFrequencyData = [];
// 12条数据累加作为一条数据
for (let i = 0; i< frequencyData.length; i += STEP) {
let sum = 0;
for(let j = i; j < i + STEP; j++) {
sum += frequencyData[j];
}
averageFrequencyData.push(sum / STEP);
}
// 长方体更具音频数据y轴动态设置放大比例
for (let i = 0; i < averageFrequencyData.length; i++) {
const scaleY = Math.floor(averageFrequencyData[i] * 0.2)
scaleY && (cubes.children[i].scale.y = scaleY);
}
renderer.render(scene, camera);
// 根据浏览器频率渲染
requestID = requestAnimationFrame(render);
}
export class StartMusic{
constructor(data = {
audioContextOrigin: null,
canvasDom: null,
}) {
audioContextOrigin = data.audioContextOrigin
if (!audioContextOrigin) {
throw new Error('缺少音乐数据源')
}
if (!data.canvasDom) {
throw new Error('缺少渲染dom')
}
create(data.canvasDom);
this.play()
}
play() {
render();
}
paused() {
cancelAnimationFrame(requestID);
}
}
更新watch方法
watch: {
isbtnShow: {
handler(val) {
if (!val) {
this.$nextTick(() => {
this.initMusicEffect();
this.initMusicThree()
});
}
else {
if (this.musicEffect) {
this.musicEffect.paused();
}
if (this.musicThree) {
this.musicThree.paused();
}
}
},
immediate: true
}
},
methods新增方法
// three.js实现3d音频可视化
initMusicThree() {
if (!this.musicOriginData) {
this.initMusicOrigin()
}
if (this.musicThree) {
this.musicThree.play()
} else {
this.musicThree = new StartMusic({
audioContextOrigin: this.musicOriginData,
canvasDom: this.$refs.threeContainerRef
})
}
}