video-play.tsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import { defineComponent, nextTick, onMounted, reactive, toRefs } from 'vue';
  2. import TCPlayer from 'tcplayer.js';
  3. import 'tcplayer.js/dist/tcplayer.min.css';
  4. // import 'plyr/dist/plyr.css';
  5. // import Plyr from 'plyr';
  6. import { ref } from 'vue';
  7. import styles from './video.module.less';
  8. import iconplay from '../image/icon-pause.png';
  9. import iconpause from '../image/icon-play.png';
  10. import iconReplay from '../image/icon-replay.png';
  11. import { NSlider } from 'naive-ui';
  12. export default defineComponent({
  13. name: 'video-play',
  14. props: {
  15. item: {
  16. type: Object,
  17. default: () => {
  18. return {};
  19. }
  20. },
  21. isEmtry: {
  22. type: Boolean,
  23. default: false
  24. }
  25. },
  26. emits: ['loadedmetadata', 'togglePlay', 'ended', 'reset'],
  27. setup(props, { emit, expose }) {
  28. const { item, isEmtry } = toRefs(props);
  29. const videoFroms = reactive({
  30. paused: true,
  31. currentTimeNum: 0,
  32. currentTime: '00:00',
  33. durationNum: 0,
  34. duration: '00:00',
  35. showBar: true
  36. });
  37. const videoRef = ref();
  38. const videoItem = ref();
  39. const videoID = 'video' + Date.now() + Math.floor(Math.random() * 100);
  40. // 对时间进行格式化
  41. const timeFormat = (num: number) => {
  42. if (num > 0) {
  43. const m = Math.floor(num / 60);
  44. const s = num % 60;
  45. return (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
  46. } else {
  47. return '00:00';
  48. }
  49. };
  50. //
  51. const toggleHideControl = (isShow: false) => {
  52. videoFroms.showBar = isShow;
  53. };
  54. const onReplay = () => {
  55. if (!videoItem.value) return;
  56. videoItem.value.currentTime(0);
  57. };
  58. // 切换音频播放
  59. const onToggleVideo = (e?: MouseEvent) => {
  60. e?.stopPropagation();
  61. if (videoFroms.paused) {
  62. videoItem.value.play();
  63. videoFroms.paused = false;
  64. } else {
  65. videoItem.value.pause();
  66. videoFroms.paused = true;
  67. }
  68. emit('togglePlay', videoFroms.paused);
  69. };
  70. onMounted(() => {
  71. videoItem.value = TCPlayer(videoID, {
  72. appID: '',
  73. controls: false
  74. }); // player-container-id 为播放器容器 ID,必须与 html 中一致
  75. if (videoItem.value) {
  76. videoItem.value.src(isEmtry.value ? '' : item.value.content); // url 播放地址
  77. // 初步加载时
  78. videoItem.value.one('loadedmetadata', () => {
  79. console.log(' Loading metadata');
  80. // 获取时长
  81. videoFroms.duration = timeFormat(
  82. Math.round(videoItem.value.duration())
  83. );
  84. videoFroms.durationNum = videoItem.value.duration();
  85. emit('loadedmetadata', videoItem.value);
  86. });
  87. // 视频播放时加载
  88. videoItem.value.on('timeupdate', () => {
  89. videoFroms.currentTime = timeFormat(
  90. Math.round(videoItem.value?.currentTime() || 0)
  91. );
  92. videoFroms.currentTimeNum = videoItem.value.currentTime();
  93. });
  94. // 视频播放结束
  95. videoItem.value.on('ended', () => {
  96. emit('ended');
  97. });
  98. }
  99. });
  100. expose({
  101. // changePlayBtn,
  102. toggleHideControl
  103. });
  104. return () => (
  105. <div class={styles.videoWrap}>
  106. <video
  107. style={{ width: '100%', height: '100%' }}
  108. src={isEmtry.value ? '' : item.value.content}
  109. ref={videoRef}
  110. id={videoID}
  111. preload="auto"
  112. playsinline
  113. webkit-playsinline></video>
  114. <div
  115. class={[
  116. styles.controls,
  117. videoFroms.showBar ? '' : styles.sectionAnimate
  118. ]}
  119. onClick={(e: MouseEvent) => {
  120. e.stopPropagation();
  121. emit('reset');
  122. }}>
  123. <div class={styles.actions}>
  124. <div class={styles.actionWrap}>
  125. <button class={styles.actionBtn} onClick={onToggleVideo}>
  126. {videoFroms.paused ? (
  127. <img class={styles.playIcon} src={iconplay} />
  128. ) : (
  129. <img class={styles.playIcon} src={iconpause} />
  130. )}
  131. </button>
  132. </div>
  133. <div class={styles.time}>
  134. <div
  135. class="plyr__time plyr__time--current"
  136. aria-label="Current time">
  137. {videoFroms.currentTime}
  138. </div>
  139. <span class={styles.line}>/</span>
  140. <div
  141. class="plyr__time plyr__time--duration"
  142. aria-label="Duration">
  143. {videoFroms.duration}
  144. </div>
  145. </div>
  146. </div>
  147. <div class={styles.slider}>
  148. <NSlider
  149. value={videoFroms.currentTimeNum}
  150. step={0.01}
  151. max={videoFroms.durationNum}
  152. tooltip={false}
  153. onUpdate:value={(val: number) => {
  154. videoItem.value.currentTime = val;
  155. videoFroms.currentTimeNum = val;
  156. videoFroms.currentTime = timeFormat(Math.round(val || 0));
  157. }}
  158. />
  159. </div>
  160. <div class={styles.actions}>
  161. <div class={styles.actionWrap}>
  162. <button class={styles.iconReplay} onClick={onReplay}>
  163. <img src={iconReplay} />
  164. </button>
  165. </div>
  166. </div>
  167. </div>
  168. </div>
  169. );
  170. }
  171. });