index.tsx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. import { defineComponent, reactive, ref, nextTick } from 'vue';
  2. import styles from './index.module.less';
  3. import iconplay from '@views/attend-class/image/icon-pause.png';
  4. import iconpause from '@views/attend-class/image/icon-play.png';
  5. import iconReplay from '@views/attend-class/image/icon-replay.png';
  6. import iconPreviewDownload from '@views/attend-class/image/icon-preivew-download.png';
  7. import { NSlider, useMessage } from 'naive-ui';
  8. import Vudio from 'vudio.js';
  9. import tickMp3 from '@views/attend-class/image/tick.mp3';
  10. import { saveAs } from 'file-saver';
  11. export default defineComponent({
  12. name: 'audio-play',
  13. props: {
  14. item: {
  15. type: Object,
  16. default: () => {
  17. return {};
  18. }
  19. },
  20. isEmtry: {
  21. type: Boolean,
  22. default: false
  23. },
  24. isDownload: {
  25. type: Boolean,
  26. default: false
  27. }
  28. },
  29. setup(props) {
  30. const message = useMessage();
  31. const audioForms = reactive({
  32. paused: true,
  33. currentTimeNum: 0,
  34. currentTime: '00:00',
  35. durationNum: 0,
  36. duration: '00:00',
  37. showBar: true,
  38. afterMa3: true
  39. });
  40. const canvas: any = ref();
  41. const audio: any = ref();
  42. let vudio: any = null;
  43. // 切换音频播放
  44. const onToggleAudio = (e?: MouseEvent) => {
  45. e?.stopPropagation();
  46. if (audio.value.paused) {
  47. onInit(audio.value, canvas.value);
  48. audio.value.play();
  49. audioForms.afterMa3 = false;
  50. } else {
  51. audio.value.pause();
  52. }
  53. audioForms.paused = audio.value.paused;
  54. };
  55. const onInit = (audio: undefined, canvas: undefined) => {
  56. if (!vudio) {
  57. vudio = new Vudio(audio, canvas, {
  58. effect: 'waveform',
  59. accuracy: 256,
  60. width: 1024,
  61. height: 600,
  62. waveform: {
  63. maxHeight: 200,
  64. color: [
  65. [0, '#44D1FF'],
  66. [0.5, '#44D1FF'],
  67. [0.5, '#198CFE'],
  68. [1, '#198CFE']
  69. ],
  70. prettify: false
  71. }
  72. });
  73. vudio.dance();
  74. }
  75. };
  76. // 对时间进行格式化
  77. const timeFormat = (num: number) => {
  78. if (num > 0) {
  79. const m = Math.floor(num / 60);
  80. const s = num % 60;
  81. return (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
  82. } else {
  83. return '00:00';
  84. }
  85. };
  86. const onReplay = () => {
  87. if (!audio.value) return;
  88. audio.value.currentTime = 0;
  89. console.log(props.item);
  90. };
  91. // 下载资源
  92. const onDownload = () => {
  93. if (!props.item.content) {
  94. message.error('下载失败');
  95. return;
  96. }
  97. const fileUrl = props.item.content;
  98. const filename = props.item.title;
  99. // 发起Fetch请求
  100. fetch(fileUrl)
  101. .then(response => response.blob())
  102. .then(blob => {
  103. saveAs(blob, filename || new Date().getTime() + '.mp3');
  104. })
  105. .catch(() => {
  106. message.error('下载失败');
  107. });
  108. };
  109. let vudio1 = null;
  110. const canvas1: any = ref();
  111. const audio1: any = ref();
  112. nextTick(() => {
  113. vudio1 = new Vudio(audio1.value, canvas1.value, {
  114. effect: 'waveform',
  115. accuracy: 256,
  116. width: 1024,
  117. height: 600,
  118. waveform: {
  119. maxHeight: 200,
  120. color: [
  121. [0, '#44D1FF'],
  122. [0.5, '#44D1FF'],
  123. [0.5, '#198CFE'],
  124. [1, '#198CFE']
  125. ],
  126. prettify: false
  127. }
  128. });
  129. vudio1.dance();
  130. });
  131. return () => (
  132. <div class={styles.audioWrap}>
  133. <div class={styles.audioContainer}>
  134. <audio
  135. ref={audio}
  136. crossorigin="anonymous"
  137. src={props.item.content + '?time=1'}
  138. onEnded={() => {
  139. audioForms.paused = true;
  140. }}
  141. onTimeupdate={() => {
  142. audioForms.currentTime = timeFormat(
  143. Math.floor(audio.value?.currentTime || 0)
  144. );
  145. audioForms.currentTimeNum = audio.value.currentTime;
  146. }}
  147. onLoadedmetadata={() => {
  148. audioForms.duration = timeFormat(
  149. Math.floor(audio.value.duration)
  150. );
  151. audioForms.durationNum = audio.value.duration;
  152. }}></audio>
  153. <canvas ref={canvas}></canvas>
  154. {audioForms.afterMa3 && (
  155. <div class={styles.tempVudio}>
  156. <audio ref={audio1} src={tickMp3} />
  157. <canvas ref={canvas1}></canvas>
  158. </div>
  159. )}
  160. </div>
  161. <div
  162. class={[
  163. styles.controls,
  164. audioForms.showBar ? '' : styles.sectionAnimate
  165. ]}
  166. onClick={(e: MouseEvent) => {
  167. e.stopPropagation();
  168. }}>
  169. <div class={styles.actions}>
  170. <div class={styles.actionWrap}>
  171. <button class={styles.actionBtn} onClick={onToggleAudio}>
  172. {audioForms.paused ? (
  173. <img class={styles.playIcon} src={iconplay} />
  174. ) : (
  175. <img class={styles.playIcon} src={iconpause} />
  176. )}
  177. </button>
  178. </div>
  179. <div class={styles.time}>
  180. <div
  181. class="plyr__time plyr__time--current"
  182. aria-label="Current time">
  183. {audioForms.currentTime}
  184. </div>
  185. <span class={styles.line}>/</span>
  186. <div
  187. class="plyr__time plyr__time--duration"
  188. aria-label="Duration">
  189. {audioForms.duration}
  190. </div>
  191. </div>
  192. </div>
  193. <div class={styles.slider}>
  194. <NSlider
  195. value={audioForms.currentTimeNum}
  196. step={0.01}
  197. max={audioForms.durationNum}
  198. tooltip={false}
  199. onUpdate:value={(val: number) => {
  200. audio.value.currentTime = val;
  201. audioForms.currentTimeNum = val;
  202. audioForms.currentTime = timeFormat(Math.round(val || 0));
  203. }}
  204. />
  205. </div>
  206. <div class={styles.actions}>
  207. <div class={styles.actionWrap}>
  208. <button class={styles.iconReplay} onClick={onReplay}>
  209. <img src={iconReplay} />
  210. </button>
  211. {props.isDownload && (
  212. <button
  213. class={styles.iconReplay}
  214. onClick={onDownload}
  215. style={{ marginLeft: '12px' }}>
  216. <img src={iconPreviewDownload} />
  217. </button>
  218. )}
  219. </div>
  220. </div>
  221. </div>
  222. </div>
  223. );
  224. }
  225. });