VideoPlayer.vue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. <template>
  2. <div class="video-player-container">
  3. <video
  4. ref="videoElement"
  5. class="video-js vjs-default-skin vjs-big-play-centered"
  6. preload="auto"
  7. :poster="poster"
  8. >
  9. <source :src="src" :type="type">
  10. <!-- <track
  11. v-if="subtitle"
  12. kind="subtitles"
  13. :src="subtitle"
  14. srclang="zh-CN"
  15. label="中文"
  16. default
  17. > -->
  18. 您的浏览器不支持HTML5视频播放
  19. </video>
  20. </div>
  21. </template>
  22. <script setup>
  23. import { ref, onMounted, onUnmounted, watch } from 'vue'
  24. import videojs from 'video.js'
  25. import 'video.js/dist/video-js.css'
  26. // 组件属性
  27. const props = defineProps({
  28. // 视频地址
  29. src: {
  30. type: String,
  31. required: true
  32. },
  33. // 视频封面
  34. poster: {
  35. type: String,
  36. default: ''
  37. },
  38. // 视频类型
  39. type: {
  40. type: String,
  41. default: 'video/mp4'
  42. },
  43. // 是否自动播放
  44. autoplay: {
  45. type: Boolean,
  46. default: false
  47. },
  48. // 是否显示控件
  49. controls: {
  50. type: Boolean,
  51. default: true
  52. },
  53. // 是否自适应容器大小
  54. fluid: {
  55. type: Boolean,
  56. default: true
  57. },
  58. // 预加载方式
  59. preload: {
  60. type: String,
  61. default: 'auto'
  62. },
  63. // 字幕文件地址
  64. subtitle: {
  65. type: String,
  66. default: ''
  67. },
  68. // 播放速度选项
  69. playbackRates: {
  70. type: Array,
  71. default: () => [ 0.75, 1, 1.25, 1.5, 2]
  72. },
  73. // 自定义控件配置
  74. controlBar: {
  75. type: Object,
  76. default: () => ({
  77. // timeDivider: true,
  78. // durationDisplay: true,
  79. // remainingTimeDisplay: false,
  80. // fullscreenToggle: true,
  81. currentTimeDisplay:true,
  82. timeDivider:true,
  83. durationDisplay:true,
  84. remainingTimeDisplay:false,
  85. volumePanel: {
  86. inline: false,
  87. },
  88. children: [
  89. {name: 'playToggle'}, // 播放按钮
  90. {name: 'currentTimeDisplay'}, // 当前已播放时间
  91. {name: 'timeDivider'}, // 总时间
  92. {name: 'durationDisplay'}, // 总时间
  93. // {name: 'remainingTimeDisplay'}, // 剩余时间
  94. {name: 'progressControl'}, // 播放进度条
  95. {
  96. name: 'volumePanel', // 音量控制
  97. inline: false, // 不使用水平方式
  98. },
  99. {name: 'FullscreenToggle'}
  100. ]
  101. })
  102. },
  103. // 视频高度(可选,支持固定高度)
  104. height: {
  105. type: String,
  106. default: 'auto'
  107. }
  108. })
  109. // 组件事件
  110. const emit = defineEmits([
  111. 'play', // 播放事件
  112. 'pause', // 暂停事件
  113. 'ended', // 播放结束事件
  114. 'timeupdate', // 播放时间更新事件
  115. 'loadedmetadata',// 元数据加载完成事件
  116. 'error', // 错误事件
  117. 'ready' // 播放器就绪事件
  118. ])
  119. const videoElement = ref(null)
  120. let player = null
  121. // 初始化播放器
  122. onMounted(() => {
  123. initPlay();
  124. })
  125. // 监听src变化,更新视频源
  126. watch(() => props.src, (newSrc, oldSrc) => {
  127. console.log('newSrc', newSrc, 'oldSrc', oldSrc)
  128. if (player && newSrc !== oldSrc && newSrc !== '') {
  129. player.src({ src: newSrc, type: props.type })
  130. player.load()
  131. if (props.autoplay) {
  132. player.play()
  133. }
  134. }
  135. })
  136. // 监听poster变化,更新封面
  137. watch(() => props.poster, (newPoster) => {
  138. if (player) {
  139. player.poster(newPoster)
  140. }
  141. })
  142. const initPlay = ()=>{
  143. if (player) {
  144. player.dispose()
  145. player = null
  146. }
  147. // 创建video.js播放器实例
  148. player = videojs(videoElement.value, {
  149. autoplay: props.autoplay,
  150. controls: props.controls,
  151. fluid: props.fluid,
  152. preload: props.preload,
  153. aspectRatio: "16:9",
  154. // playbackRates: props.playbackRates,// 播放速度选项
  155. sources: [{ src: props.src, type: props.type }], // 添加这行
  156. controlBar: props.controlBar,
  157. })
  158. // 绑定事件
  159. player.on('play', () => emit('play'))
  160. player.on('pause', () => emit('pause'))
  161. player.on('ended', () => emit('ended'))
  162. player.on('timeupdate', () => emit('timeupdate', player.currentTime()))
  163. player.on('loadedmetadata', () => emit('loadedmetadata', player.duration()))
  164. player.on('error', () => emit('error', player.error()))
  165. player.on('ready', () => emit('ready', player))
  166. }
  167. // 组件销毁时清理播放器
  168. onUnmounted(() => {
  169. if (player) {
  170. player.dispose()
  171. player = null
  172. }
  173. })
  174. // 暴露方法给父组件
  175. defineExpose({
  176. // 播放
  177. play: () => player && player.play(),
  178. // 暂停
  179. pause: () => player && player.pause(),
  180. // 切换播放状态
  181. togglePlay: () => player && (player.paused() ? player.play() : player.pause()),
  182. // 跳转到指定时间
  183. seekTo: (time) => player && player.currentTime(time),
  184. // 获取当前播放时间
  185. getCurrentTime: () => player && player.currentTime(),
  186. // 获取视频总时长
  187. getDuration: () => player && player.duration(),
  188. // 判断是否正在播放
  189. isPlaying: () => player && !player.paused(),
  190. // 设置音量
  191. setVolume: (volume) => player && player.volume(volume),
  192. // 获取音量
  193. getVolume: () => player && player.volume(),
  194. // 设置播放速度
  195. setPlaybackRate: (rate) => player && player.playbackRate(rate),
  196. // 获取播放速度
  197. getPlaybackRate: () => player && player.playbackRate(),
  198. // 获取播放器实例(高级用法)
  199. getPlayerInstance: () => player,
  200. initPlay,
  201. });
  202. </script>
  203. <style scoped lang="scss">
  204. .video-player-container {
  205. width: 100%;
  206. border-radius: 8px;
  207. overflow: hidden;
  208. :deep(.video-js) {
  209. width: 100%;
  210. // height: 500px;
  211. border-radius: 8px;
  212. background-color: #000;
  213. // 自定义当前时间和总时间显示
  214. .vjs-current-time{
  215. display: block;
  216. }
  217. // 自定义总时间显示
  218. .vjs-duration{
  219. display: block;
  220. }
  221. // 自定义总时间和当前时间之间的分隔符
  222. .vjs-time-divider{
  223. display: block;
  224. }
  225. // 自定义大播放按钮样式
  226. .vjs-big-play-button {
  227. // width: 80px;
  228. // height: 80px;
  229. // line-height: 80px;
  230. // border-radius: 50%;
  231. // font-size: 30px;
  232. // border: none;
  233. // background-color: rgba(0, 0, 0, 0.6);
  234. // &:hover {
  235. // background-color: rgba(0, 0, 0, 0.8);
  236. // }
  237. }
  238. // 自定义控制栏样式
  239. .vjs-control-bar {
  240. // background: linear-gradient(to top, rgba(0, 0, 0, 0.7) 0%, rgba(0, 0, 0, 0) 100%);
  241. // height: 60px;
  242. // padding: 0 10px;
  243. }
  244. // 自定义进度条样式
  245. .vjs-progress-control {
  246. // position: absolute;
  247. // top: 0;
  248. // left: 0;
  249. // width: 100%;
  250. // height: 5px;
  251. // margin: 0;
  252. // .vjs-progress-holder {
  253. // height: 100%;
  254. // .vjs-play-progress {
  255. // background-color: #409eff;
  256. // }
  257. // }
  258. }
  259. }
  260. }
  261. </style>