audio-pay.tsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import { defineComponent, reactive, ref, nextTick } from 'vue';
  2. import styles from './audio.module.less';
  3. import iconplay from '../image/icon-pause.svg';
  4. import iconpause from '../image/icon-play.svg';
  5. import iconReplay from '../image/icon-replay.svg';
  6. import { NSlider } from 'naive-ui';
  7. import Vudio from 'vudio.js';
  8. import tickMp3 from '../image/tick.mp3';
  9. export default defineComponent({
  10. name: 'audio-play',
  11. props: {
  12. item: {
  13. type: Object,
  14. default: () => {
  15. return {};
  16. }
  17. },
  18. isEmtry: {
  19. type: Boolean,
  20. default: false
  21. }
  22. },
  23. emits: ['loadedmetadata', 'togglePlay', 'ended', 'reset'],
  24. setup(props, { emit, expose }) {
  25. const audioForms = reactive({
  26. paused: true,
  27. currentTimeNum: 0,
  28. currentTime: '00:00',
  29. durationNum: 0,
  30. duration: '00:00',
  31. showBar: true,
  32. afterMa3: true
  33. });
  34. const canvas: any = ref();
  35. const audio: any = ref();
  36. let vudio: any = null;
  37. // 切换音频播放
  38. const onToggleAudio = (e?: MouseEvent) => {
  39. e?.stopPropagation();
  40. if (audio.value.paused) {
  41. onInit(audio.value, canvas.value);
  42. audio.value.play();
  43. audioForms.afterMa3 = false;
  44. } else {
  45. audio.value.pause();
  46. }
  47. audioForms.paused = audio.value.paused;
  48. emit('togglePlay', audioForms.paused);
  49. };
  50. const onInit = (audio: undefined, canvas: undefined) => {
  51. if (!vudio) {
  52. vudio = new Vudio(audio, canvas, {
  53. effect: 'waveform',
  54. accuracy: 256,
  55. width: 1024,
  56. height: 600,
  57. waveform: {
  58. maxHeight: 200,
  59. color: [
  60. [0, '#44D1FF'],
  61. [0.5, '#44D1FF'],
  62. [0.5, '#198CFE'],
  63. [1, '#198CFE']
  64. ],
  65. prettify: false
  66. }
  67. });
  68. vudio.dance();
  69. }
  70. };
  71. // 对时间进行格式化
  72. const timeFormat = (num: number) => {
  73. if (num > 0) {
  74. const m = Math.floor(num / 60);
  75. const s = num % 60;
  76. return (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
  77. } else {
  78. return '00:00';
  79. }
  80. };
  81. //
  82. const toggleHideControl = (isShow: false) => {
  83. audioForms.showBar = isShow;
  84. };
  85. const onReplay = () => {
  86. if (!audio.value) return;
  87. audio.value.currentTime = 0;
  88. };
  89. let vudio1 = null;
  90. const canvas1: any = ref();
  91. const audio1: any = ref();
  92. nextTick(() => {
  93. vudio1 = new Vudio(audio1.value, canvas1.value, {
  94. effect: 'waveform',
  95. accuracy: 256,
  96. width: 1024,
  97. height: 600,
  98. waveform: {
  99. maxHeight: 200,
  100. color: [
  101. [0, '#44D1FF'],
  102. [0.5, '#44D1FF'],
  103. [0.5, '#198CFE'],
  104. [1, '#198CFE']
  105. ],
  106. prettify: false
  107. }
  108. });
  109. vudio1.dance();
  110. });
  111. expose({
  112. toggleHideControl
  113. });
  114. return () => (
  115. <div class={styles.audioWrap}>
  116. <div class={styles.audioContainer}>
  117. <audio
  118. ref={audio}
  119. crossorigin="anonymous"
  120. src={props.item.content + '?time=1'}
  121. onEnded={() => {
  122. audioForms.paused = true;
  123. emit('ended');
  124. }}
  125. onTimeupdate={() => {
  126. audioForms.currentTime = timeFormat(
  127. Math.round(audio.value?.currentTime || 0)
  128. );
  129. audioForms.currentTimeNum = audio.value.currentTime;
  130. }}
  131. onLoadedmetadata={() => {
  132. audioForms.duration = timeFormat(
  133. Math.round(audio.value.duration)
  134. );
  135. audioForms.durationNum = audio.value.duration;
  136. if (props.item.autoPlay && audio.value) {
  137. audio.value.play();
  138. }
  139. audio.value.stop = () => {
  140. audio.value.pause();
  141. audioForms.paused = true;
  142. emit('togglePlay', audioForms.paused);
  143. };
  144. audio.value.onPlay = () => {
  145. audio.value.play();
  146. audioForms.paused = false;
  147. onInit(audio.value, canvas.value);
  148. emit('togglePlay', audioForms.paused);
  149. };
  150. emit('loadedmetadata', audio.value);
  151. }}></audio>
  152. <canvas ref={canvas}></canvas>
  153. {audioForms.afterMa3 && (
  154. <div class={styles.tempVudio}>
  155. <audio ref={audio1} src={tickMp3} />
  156. <canvas ref={canvas1}></canvas>
  157. </div>
  158. )}
  159. </div>
  160. <div
  161. class={[
  162. styles.controls,
  163. audioForms.showBar ? '' : styles.sectionAnimate
  164. ]}
  165. onClick={(e: MouseEvent) => {
  166. e.stopPropagation();
  167. emit('reset');
  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. </div>
  212. </div>
  213. </div>
  214. </div>
  215. );
  216. }
  217. });