如何在 Nuxt 3 中使用 wavesurfer.js
阅读原文时间:2023年07月08日阅读:3

安装 wavesurfer.js

在项目中安装 wavesurfer.js

npm install --save wavesurfer.js

常规方式引入

如果你的根目录中没有 components 目录则需要创建该目录,并在此目录中创建 WaveSurfer.vue 内容如下:

<template>
  <div ref="wavesurferMain"></div>
</template>
<script setup>
import WaveSurfer from 'wavesurfer.js'  

const props = defineProps({
  src:{
    type:String,
    required:true
  },
  options:{
    type:Object,
  }
});
const wavesurferMain = ref(null);
const waveSurfer = ref(null);  

let options = props.options;
let wsOptions = Object.assign({
      container: wavesurferMain.value
    },
    options);  

waveSurfer.value = new WaveSurfer.create(wsOptions);
waveSurfer.value.load(props.src);
</script>

然后我们集成该组件,在这个例子中我们将在 app.vue 直接引用,并且我将测试音频文件 demo.wav,放在根目录的public 中。

<template>
  <div>
    <WaveSurfer src="/demo.wav":options="waveSurferOption" />
  </div>
</template>
<script setup>
const waveSurferOption = {
  height: 340,
  progressColor: '#e03639',
  waveColor: '#e7e7e7',
  cursorColor: '#FFDDDD',
  barWidth: 2,
  mediaControls: true,
  backend: 'MediaElement',
  scrollParent:true,
  xhr: {
    mode: 'no-cors'
  }
};
</script>

现在执行 npm run dev ,页面将报错 self is not defined

这是因为在 setup 这个生命周期中,DOM 节点并未创建,所以我们需要在mounted 阶段进行导入。

正确的引入方式

更改 WaveSurfer.vue 文件内容如下:

<template>
  <div ref="wavesurferMain"></div>
</template>
<script setup>
const props = defineProps({
  src:{
    type:String,
    required:true
  },
  options:{
    type:Object,
  }
});  

const wavesurferMain = ref(null);
const waveSurfer = ref(null);  

onMounted(async ()=>{
  const WaveSurfer = (await import('wavesurfer.js')).default;
  const options = props.options;
  const wsOptions = Object.assign({
        container: wavesurferMain.value
      },
      options);
  waveSurfer.value = new WaveSurfer.create(wsOptions);
  waveSurfer.value.load(props.src);
});
</script>

现在你应该能看到已经可以正常加载了。

加载插件

加载方式和插件一样,官方的插件在 wavesurfer.js/dist/plugin 目录下,这个例子将加载时间线插件如下:

<template>
  <div ref="wavesurferMain"></div>
  <div ref="waveTimeline"></div>
</template>
<script setup>
const props = defineProps({
  src:{
    type:String,
    required:true
  },
  options:{
    type:Object,
  }
});  

const wavesurferMain = ref(null);
const waveTimeline = ref(null);
const waveSurfer = ref(null);  

onMounted(async ()=>{
  const WaveSurfer = (await import('wavesurfer.js')).default;
  const Timeline  = (await import('wavesurfer.js/dist/plugin/wavesurfer.timeline')).default;
  const options = props.options;
  const wsOptions = Object.assign({
        container: wavesurferMain.value,
        plugins:[
          Timeline.create({container:waveTimeline.value})
        ]
      },
      options);
  waveSurfer.value = new WaveSurfer.create(wsOptions);
  waveSurfer.value.load(props.src);  

});
</script>

加载波形数据

如果音频文件过大,使用插件原生的波形生成方式会非常慢。这个时候可以通过服务端生成波形数据,并让插件直接通过波形数据进行渲染。具体生成方式可以参考官方的解决方案FAQ。在这个项目中,生成波形数据文件后,我把它移动到项目的public中,更改 WaveSurfer.vue 内容如下:

<template>
  <div ref="wavesurferMain"></div>
  <div ref="waveTimeline"></div>
</template>
<script setup>
const props = defineProps({
  src:{
    type:String,
    required:true
  },
  peaksData:{
    type:String,
  },
  options:{
    type:Object,
  }
});  

const wavesurferMain = ref(null);
const waveTimeline = ref(null);
const waveSurfer = ref(null);  

onMounted(async ()=>{
  const WaveSurfer = (await import('wavesurfer.js')).default;
  const Timeline  = (await import('wavesurfer.js/dist/plugin/wavesurfer.timeline')).default;
  const options = props.options;
  const wsOptions = Object.assign({
        container: wavesurferMain.value,
        plugins:[
          Timeline.create({container:waveTimeline.value})
        ]
      },
      options);
  waveSurfer.value = new WaveSurfer.create(wsOptions);
  fetch(props.peaksData)
      .then(response => {
        if (!response.ok) {
          throw new Error("HTTP error " + response.status);
        }
        return response.json();
      })
      .then(peaks => {
        waveSurfer.value.load(props.src,peaks.data);
      })
      .catch((e) => {
        console.error('error', e);
      });  

});
</script>

app.vue 中变更如下:

<template>
  <div>
    <WaveSurfer src="/demo.wav" peaks-data="/demo.json" :options="waveSurferOption" />
  </div>
  </template>
<script setup>
const waveSurferOption = {
  height: 340,
  progressColor: '#e03639',
  waveColor: '#e7e7e7',
  cursorColor: '#FFDDDD',
  barWidth: 2,
  mediaControls: false,
  backend: 'MediaElement',
  scrollParent:true,
  xhr: {
    mode: 'no-cors'
  }
}
</script>

暴露插件的方法

现在我们只是正常初始化插件并让它加载了音频文件,目前我们并不能操作它。

因为 Vue3 中,默认并不会暴露 <script setup> 中声明的绑定。我们需要使用 defineExpose 来暴露对应的属性。WaveSurfer.vue 如下变更:

<template>
  <div ref="wavesurferMain"></div>
  <div ref="waveTimeline"></div>
</template>
<script setup>
const props = defineProps({
  src:{
    type:String,
    required:true
  },
  peaksData:{
    type:String,
  },
  options:{
    type:Object,
  }
});  

const wavesurferMain = ref(null);
const waveTimeline = ref(null);
const waveSurfer = ref(null);  

onMounted(async ()=>{
  // 省略逻辑

});
defineExpose(
    {
      waveSurfer
    }
)
</script>

app.vue 中我们可以这样调用:

<template>
  <div>
  <WaveSurfer ref="refWaveSurfer" src="/demo.wav" peaks-data="/demo.json" :options="waveSurferOption"/>
    <button @click="play">play</button>
    <button @click="pause">pause</button>
  </div>
  </template>
<script setup>
const waveSurferOption = {
  height: 340,
  progressColor: '#e03639',
  waveColor: '#e7e7e7',
  cursorColor: '#FFDDDD',
  barWidth: 2,
  mediaControls: false,
  backend: 'MediaElement',
  scrollParent:true,
  xhr: {
    mode: 'no-cors'
  }
}
const refWaveSurfer = ref(null);  

function play() {
  refWaveSurfer.value.waveSurfer.play();  // 调用播放方法
}  

function pause(){
  refWaveSurfer.value.waveSurfer.pause();  // 调用暂停方法
}
</script>

项目

你可以在以下仓库看到完整的示例

https://github.com/AnyStudy/nuxt-3-wavesurfer-demo