index.vue 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. <script setup lang="ts">
  2. import WaveSurfer from "wavesurfer.js";
  3. import { getTime } from "@pureadmin/utils";
  4. import { Play, Pause, Forward, Rewind } from "./svg";
  5. import { ref, onMounted, onBeforeUnmount } from "vue";
  6. defineOptions({
  7. name: "Wavesurfer"
  8. });
  9. const loading = ref(true);
  10. const wavesurfer = ref(null);
  11. const wavesurferRef = ref();
  12. // 音频总时长(格式化后 mm:ss)
  13. const totalTime = ref();
  14. // 音频总时长(单位秒)
  15. const totalSecondTime = ref();
  16. // 音频当前播放位置时长
  17. const curTime = ref();
  18. // 音频是否正在播放
  19. const isPlay = ref(false);
  20. const { VITE_PUBLIC_PATH } = import.meta.env;
  21. const url = `${VITE_PUBLIC_PATH}audio/海阔天空.mp3`;
  22. function init() {
  23. wavesurfer.value = WaveSurfer.create({
  24. container: wavesurferRef.value,
  25. height: "auto",
  26. waveColor: "rgb(200, 0, 200)",
  27. progressColor: "rgb(100, 0, 100)",
  28. cursorColor: "rgb(64, 158, 255)",
  29. cursorWidth: 4,
  30. // backend: "MediaElement",
  31. url
  32. });
  33. // 音频被解码后触发
  34. wavesurfer.value.on("decode", () => (loading.value = false));
  35. // 当音频已解码并可以播放时触发
  36. wavesurfer.value.on("ready", () => {
  37. if (!wavesurfer.value) return;
  38. const { decodedData } = wavesurfer.value;
  39. totalSecondTime.value = decodedData.duration;
  40. const { m, s } = getTime(decodedData.duration);
  41. totalTime.value = `${m}:${s}`;
  42. // 光标位置取中
  43. wavesurfer.value.setTime(decodedData.duration / 2);
  44. // 设置音频音量(范围0-1)
  45. // wavesurfer.value.setVolume(1);
  46. });
  47. // 音频位置改变时,播放期间连续触发
  48. wavesurfer.value.on("timeupdate", timer => {
  49. if (timer > totalSecondTime.value) return;
  50. const { m, s } = getTime(timer);
  51. curTime.value = `${m}:${s}`;
  52. });
  53. // 音频播放时触发
  54. wavesurfer.value.on("play", () => (isPlay.value = true));
  55. // 音频暂停时触发
  56. wavesurfer.value.on("pause", () => (isPlay.value = false));
  57. }
  58. onMounted(init);
  59. onBeforeUnmount(() => {
  60. if (wavesurfer.value) {
  61. wavesurfer.value.destroy();
  62. wavesurfer.value = null;
  63. }
  64. });
  65. </script>
  66. <template>
  67. <el-card shadow="never">
  68. <template #header>
  69. <div class="card-header">
  70. <span class="font-medium">
  71. 音频可视化,采用开源的
  72. <el-link
  73. href="https://wavesurfer-js.org/"
  74. target="_blank"
  75. style="margin: 0 4px 5px; font-size: 16px"
  76. >
  77. wavesurfer.js
  78. </el-link>
  79. <span class="text-[red]">
  80. (温馨提示:音频默认最大声音,播放时请调低电脑声音,以免影响到您)
  81. </span>
  82. </span>
  83. </div>
  84. <el-link
  85. class="mt-2"
  86. href="https://github.com/pure-admin/vue-pure-admin/blob/main/src/views/able/wavesurfer"
  87. target="_blank"
  88. >
  89. 代码位置 src/views/able/wavesurfer
  90. </el-link>
  91. </template>
  92. <div
  93. v-loading="loading"
  94. class="w-8/12 m-auto! mt-[20px]!"
  95. element-loading-background="transparent"
  96. >
  97. <div ref="wavesurferRef" />
  98. <div v-show="totalTime" class="flex justify-between">
  99. <span class="text-[#81888f]">00:00</span>
  100. <h1 class="text-4xl mt-2!">{{ curTime }}</h1>
  101. <span class="text-[#81888f]">{{ totalTime }}</span>
  102. </div>
  103. <div v-show="totalTime" class="flex mt-2 w-[180px] justify-around m-auto">
  104. <Rewind
  105. v-tippy="{
  106. content: '快退(可长按)',
  107. placement: 'bottom'
  108. }"
  109. v-longpress:0:100="() => wavesurfer?.skip(-1)"
  110. class="cursor-pointer"
  111. />
  112. <div
  113. v-tippy="{
  114. content: isPlay ? '暂停' : '播放',
  115. placement: 'bottom'
  116. }"
  117. class="cursor-pointer"
  118. @click="wavesurfer?.playPause()"
  119. >
  120. <Play v-if="isPlay" v-motion-pop />
  121. <Pause v-else v-motion-pop />
  122. </div>
  123. <Forward
  124. v-tippy="{
  125. content: '快进(可长按)',
  126. placement: 'bottom'
  127. }"
  128. v-longpress:0:100="() => wavesurfer?.skip(1)"
  129. class="cursor-pointer"
  130. />
  131. </div>
  132. </div>
  133. </el-card>
  134. </template>