index.vue 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  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. import "tippy.js/dist/tippy.css";
  7. import "tippy.js/animations/scale.css";
  8. import { directive as tippy } from "vue-tippy";
  9. defineOptions({
  10. name: "Wavesurfer"
  11. });
  12. const vTippy = tippy;
  13. const loading = ref(true);
  14. const wavesurfer = ref(null);
  15. const wavesurferRef = ref();
  16. // 音频总时长
  17. const totalTime = ref();
  18. // 音频当前播放位置时长
  19. const curTime = ref();
  20. // 音频是否正在播放
  21. const isPlay = ref(false);
  22. const { VITE_PUBLIC_PATH } = import.meta.env;
  23. const url = `${VITE_PUBLIC_PATH}audio/海阔天空.mp3`;
  24. function init() {
  25. wavesurfer.value = WaveSurfer.create({
  26. container: wavesurferRef.value,
  27. height: "auto",
  28. waveColor: "rgb(200, 0, 200)",
  29. progressColor: "rgb(100, 0, 100)",
  30. cursorColor: "rgb(64, 158, 255)",
  31. cursorWidth: 4,
  32. // backend: "MediaElement",
  33. url
  34. });
  35. // 音频被解码后触发
  36. wavesurfer.value.on("decode", () => (loading.value = false));
  37. // 当音频已解码并可以播放时触发
  38. wavesurfer.value.on("ready", () => {
  39. if (!wavesurfer.value) return;
  40. const { duration } = wavesurfer.value;
  41. const { m, s } = getTime(duration);
  42. totalTime.value = `${m}:${s}`;
  43. // 光标位置取中
  44. wavesurfer.value.setTime(duration / 2);
  45. // 设置音频音量(范围0-1)
  46. // wavesurfer.value.setVolume(1);
  47. });
  48. // 音频位置改变时,播放期间连续触发
  49. wavesurfer.value.on("timeupdate", timer => {
  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. </template>
  85. <div
  86. v-loading="loading"
  87. class="w-8/12 !m-auto !mt-[20px]"
  88. element-loading-background="transparent"
  89. >
  90. <div ref="wavesurferRef" />
  91. <div class="flex justify-between" v-show="totalTime">
  92. <span class="text-[#81888f]">00:00</span>
  93. <h1 class="text-4xl mt-2">{{ curTime }}</h1>
  94. <span class="text-[#81888f]">{{ totalTime }}</span>
  95. </div>
  96. <div class="flex mt-2 w-[180px] justify-around m-auto" v-show="totalTime">
  97. <Rewind
  98. class="cursor-pointer"
  99. v-tippy="{
  100. content: '快退(可长按)',
  101. placement: 'bottom',
  102. animation: 'scale'
  103. }"
  104. v-longpress:0:100="() => wavesurfer?.skip(-1)"
  105. />
  106. <div
  107. class="cursor-pointer"
  108. v-tippy="{
  109. content: isPlay ? '暂停' : '播放',
  110. placement: 'bottom',
  111. animation: 'scale'
  112. }"
  113. @click="wavesurfer?.playPause()"
  114. >
  115. <Play v-if="isPlay" v-motion-pop />
  116. <Pause v-else v-motion-pop />
  117. </div>
  118. <Forward
  119. class="cursor-pointer"
  120. v-tippy="{
  121. content: '快进(可长按)',
  122. placement: 'bottom',
  123. animation: 'scale'
  124. }"
  125. v-longpress:0:100="() => wavesurfer?.skip(1)"
  126. />
  127. </div>
  128. </div>
  129. </el-card>
  130. </template>