index.tsx 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. import { defineComponent, nextTick, reactive, toRefs, watch } from 'vue';
  2. import { ref } from 'vue';
  3. import styles from './index.module.less';
  4. import iconLoop from '../../image/icon-loop.png';
  5. import iconLoopActive from '../../image/icon-loop-active.png';
  6. import iconplay from '../../image/icon-play.png';
  7. import iconpause from '../../image/icon-pause.png';
  8. import { NSlider } from 'naive-ui';
  9. import Vudio from 'vudio.js';
  10. import { getSecondRPM } from '@/helpers/utils';
  11. import { Slider, showToast } from 'vant';
  12. import tickMp3 from '../../image/tick.mp3';
  13. export default defineComponent({
  14. name: 'video-play',
  15. props: {
  16. item: {
  17. type: Object,
  18. default: () => {
  19. return {};
  20. }
  21. },
  22. show: {
  23. type: Boolean,
  24. default: false
  25. },
  26. pageVisibility: {
  27. type: String,
  28. default: ''
  29. },
  30. showModel: {
  31. type: Boolean,
  32. default: false
  33. },
  34. isEmtry: {
  35. type: Boolean,
  36. default: false
  37. }
  38. },
  39. emits: ['loadedmetadata', 'togglePlay', 'ended', 'reset', 'close'],
  40. setup(props, { emit }) {
  41. const { item, isEmtry } = toRefs(props);
  42. const data = reactive({
  43. timer: null as any,
  44. currentTime: 0,
  45. duration: 0.1,
  46. loop: false,
  47. dragStatus: false, // 是否开始拖动
  48. playState: 'pause' as 'play' | 'pause',
  49. vudio: null as any,
  50. afterMa3: true,
  51. count: 0
  52. });
  53. const canvasRef: any = ref();
  54. const audioRef: any = ref();
  55. const contetRef = ref();
  56. watch(
  57. () => props.show,
  58. val => {
  59. data.count = 0;
  60. if (val) {
  61. // data.count = 0;
  62. // audioRef.value.src = item.value.content;
  63. onToggleAudio();
  64. } else {
  65. audioRef.value.pause();
  66. // audioRef.value.src = '';
  67. data.playState = 'pause';
  68. // data.vudio = null;
  69. }
  70. }
  71. );
  72. // watch(
  73. // () => props.item,
  74. // () => {
  75. // // data.count = 0;
  76. // // audioRef.value.src = item.value.content;
  77. // onToggleAudio();
  78. // }
  79. // );
  80. watch(
  81. () => props.pageVisibility,
  82. val => {
  83. if (val === 'hidden' && props.show) {
  84. audioRef.value.pause();
  85. data.playState = 'pause';
  86. }
  87. }
  88. );
  89. // 切换音频播放
  90. const onToggleAudio = () => {
  91. if (audioRef.value.paused) {
  92. audioRef.value.play();
  93. data.playState = 'play';
  94. //
  95. nextTick(() => {
  96. // onInit(audioRef.value, canvasRef.value);
  97. // setTimeout(() => {
  98. // onInit(audioRef.value, canvasRef.value);
  99. // }, 100);
  100. // console.log(data.vudio, 'data.vudio');
  101. // if (data.vudio) {
  102. // data.vudio.dance();
  103. // }
  104. });
  105. } else {
  106. audioRef.value.pause();
  107. data.playState = 'pause';
  108. }
  109. };
  110. const onInit = (audio: undefined, canvas: undefined) => {
  111. if (!data.vudio && data.count > 1) {
  112. console.log(data.vudio, 'data.vudio');
  113. try {
  114. const rect = contetRef.value.getBoundingClientRect() || {
  115. width: 200,
  116. height: 200
  117. };
  118. data.vudio = new Vudio(audio, canvas, {
  119. effect: 'waveform',
  120. accuracy: 128,
  121. width: rect.width,
  122. height: rect.height,
  123. waveform: {
  124. maxHeight: 160,
  125. color: [
  126. [0, '#44D1FF'],
  127. [0.5, '#44D1FF'],
  128. [0.5, '#198CFE'],
  129. [1, '#198CFE']
  130. ],
  131. prettify: false
  132. }
  133. });
  134. data.vudio.dance();
  135. } catch (e) {
  136. console.log(e, 'e');
  137. }
  138. } else {
  139. }
  140. };
  141. /** 加载成功 */
  142. const onLoadedmetadata = () => {
  143. data.duration = audioRef.value?.duration;
  144. // onInit(audioRef.value, canvasRef.value);
  145. if (props.item.autoPlay && audioRef.value && props.show) {
  146. // data.vudio = null;
  147. // data.count = 0;
  148. onToggleAudio();
  149. }
  150. // if (audioRef.value) {
  151. // audioRef.value.stop = () => {
  152. // audioRef.value?.pause();
  153. // };
  154. // audioRef.value.onPlay = () => {
  155. // audioRef.value?.play();
  156. // onInit(audioRef.value, canvasRef.value);
  157. // };
  158. // }
  159. };
  160. /** 改变播放时间 */
  161. const handleChangeTime = (val: number) => {
  162. data.currentTime = val;
  163. clearTimeout(data.timer);
  164. data.timer = setTimeout(() => {
  165. audioRef.value.currentTime = val;
  166. data.timer = null;
  167. }, 60);
  168. };
  169. /** 播放结束 */
  170. const onEnded = () => {
  171. data.playState = 'pause';
  172. // emit('ended');
  173. };
  174. let vudio1 = null;
  175. const canvas1: any = ref();
  176. const audio1: any = ref();
  177. nextTick(() => {
  178. const rect = contetRef.value.getBoundingClientRect() || {
  179. width: 200,
  180. height: 200
  181. };
  182. vudio1 = new Vudio(audio1.value, canvas1.value, {
  183. effect: 'waveform',
  184. accuracy: 128,
  185. width: rect.width,
  186. height: rect.height,
  187. waveform: {
  188. maxHeight: 160,
  189. color: [
  190. [0, '#44D1FF'],
  191. [0.5, '#44D1FF'],
  192. [0.5, '#198CFE'],
  193. [1, '#198CFE']
  194. ],
  195. prettify: false
  196. }
  197. });
  198. vudio1.dance();
  199. });
  200. return () => (
  201. <div class={styles.videoWrap}>
  202. <div class={styles.content}>
  203. <div ref={contetRef} class={styles.contentWrap}>
  204. <canvas ref={canvasRef}></canvas>
  205. <audio
  206. src={isEmtry.value ? '' : item.value.content}
  207. ref={audioRef}
  208. loop={data.loop}
  209. onLoadedmetadata={onLoadedmetadata}
  210. onTimeupdate={() => {
  211. if (data.timer && data.playState === 'pause') return;
  212. if (data.count <= 1) {
  213. data.count += 1;
  214. onInit(audioRef.value, canvasRef.value);
  215. }
  216. // 开始拖动时也不能更新
  217. if (data.dragStatus) return;
  218. data.currentTime = audioRef.value?.currentTime;
  219. }}
  220. onError={() => {
  221. console.log('error');
  222. audioRef.value.pause();
  223. data.playState = 'pause';
  224. }}
  225. onPause={() => {
  226. console.log('pause');
  227. data.playState = 'pause';
  228. }}
  229. onEnded={onEnded}
  230. crossorigin="anonymous"
  231. playsinline="false"></audio>
  232. {data.afterMa3 && (
  233. <div class={styles.tempVudio}>
  234. <audio ref={audio1} src={tickMp3} />
  235. <canvas ref={canvas1}></canvas>
  236. </div>
  237. )}
  238. </div>
  239. </div>
  240. <div
  241. class={[styles.controls, props.showModel ? '' : styles.hide]}
  242. onClick={(e: Event) => {
  243. e.stopPropagation();
  244. }}
  245. onTouchmove={(e: TouchEvent) => {
  246. emit('close');
  247. }}>
  248. <div class={styles.time}>
  249. <div>{getSecondRPM(data.currentTime)}</div>
  250. <div>{getSecondRPM(data.duration)}</div>
  251. </div>
  252. <div class={[styles.slider]}>
  253. {/* <NSlider
  254. tooltip={false}
  255. step={0.01}
  256. class={styles.timeProgress}
  257. value={data.currentTime}
  258. max={data.duration}
  259. onUpdate:value={val => handleChangeTime(val)}
  260. /> */}
  261. <Slider
  262. step={0.01}
  263. class={styles.timeProgress}
  264. v-model={data.currentTime}
  265. max={data.duration}
  266. onUpdate:modelValue={val => {
  267. handleChangeTime(val);
  268. }}
  269. onDragStart={() => {
  270. data.dragStatus = true;
  271. console.log('onDragStart');
  272. }}
  273. onDragEnd={() => {
  274. data.dragStatus = false;
  275. console.log('onDragEnd');
  276. }}
  277. />
  278. </div>
  279. <div class={styles.actions} onClick={() => emit('close')}>
  280. <div class={styles.actionBtn} onClick={() => onToggleAudio()}>
  281. <img src={data.playState === 'pause' ? iconplay : iconpause} />
  282. </div>
  283. <div
  284. class={styles.actionBtn}
  285. onClick={() => {
  286. data.loop = !data.loop;
  287. if (data.loop) {
  288. setTimeout(() => {
  289. showToast('已打开循环播放');
  290. }, 60);
  291. } else {
  292. setTimeout(() => {
  293. showToast('已关闭循环播放');
  294. }, 60);
  295. }
  296. }}>
  297. <img src={data.loop ? iconLoopActive : iconLoop} />
  298. </div>
  299. </div>
  300. </div>
  301. </div>
  302. );
  303. }
  304. });