mx-video.vue 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. <template>
  2. <video id="mxVideo" :src="safeUrl" controls :poster="coverUrl" object-fit="contain"
  3. @error="handleError" @timeupdate="handlePlaying" @ended="handlePlayEnd"
  4. @fullscreenchange="handleFullscreenChange"
  5. class="w-full aspect-video" v-bind="$attrs"></video>
  6. <view class="bg-white fx-row items-center px-30 gap-30 h-80">
  7. <text class="text-tips text-sm">倍速播放</text>
  8. <view class="fx-row items-center gap-10">
  9. <uv-tags v-for="rate in supportRates" :type="videoCtx?'primary':'info'" :text="`x`+rate"
  10. :plain="currentRate!=rate" size="mini" class="rate-item" @click="handleRate(rate)">
  11. </uv-tags>
  12. </view>
  13. </view>
  14. <uv-line/>
  15. </template>
  16. <script setup>
  17. import {computed, onUnmounted, ref} from 'vue'
  18. import {useEnvStore} from "@/hooks/useEnvStore";
  19. import {useVideoPlay} from "@/components/mx-video/useVideoPlay";
  20. import {useVideoRecord} from "@/components/mx-video/useVideoRecord";
  21. import {createPropDefine} from "@/utils";
  22. const props = defineProps({
  23. aliIdType: createPropDefine('', [String, Number]),
  24. src: createPropDefine(''),
  25. // 某些场景下,不需要全屏控制,
  26. // 因为视频可能是纯为手机端录制的,不需要旋转
  27. disabledFullScreen: createPropDefine(false, Boolean)
  28. })
  29. const emits = defineEmits(['change', 'error'])
  30. const videoCtx = ref(null)
  31. const currentRate = ref(1)
  32. const {safeUrl, coverUrl, onSrcChange} = useVideoPlay(props)
  33. const {reset: resetRecord, commit: commitRecord, syncProgress} = useVideoRecord()
  34. const {isAndroid, isIOS} = useEnvStore()
  35. const supportRates = computed(() => isAndroid.value ? [0.5, 0.8, 1.0, 1.25, 1.5] : [0.5, 1.0, 1.25, 1.5, 2.0])
  36. // const rateCtrlStyle = computed(() => isIOS.value
  37. // ? {marginTop: '-5px', height: '39.5px',}
  38. // : {marginTop: '-5px', height: '40px'})
  39. onSrcChange(() => {
  40. // 切换时,创建速度控制
  41. videoCtx.value = uni.createVideoContext('mxVideo')
  42. currentRate.value = 1
  43. })
  44. onSrcChange(async (newVal, oldVal) => {
  45. // 切换时,试图提交前一个视频的观看记录
  46. await commitRecord(oldVal)
  47. // 之后,重置记录功能
  48. resetRecord()
  49. })
  50. // 预留事件,向外抛出change事件
  51. onSrcChange((newVal, oldVal) => emits('change', newVal, oldVal))
  52. // 退出时,试图提交观看记录
  53. onUnmounted(() => commitRecord(props))
  54. // 播放完毕时,强制提交
  55. const handlePlayEnd = () => commitRecord(props)
  56. // 播放过程中,本地同步播放进度
  57. const handlePlaying = (e) => syncProgress(e.detail)
  58. const handleFullscreenChange = (e) => {
  59. if (props.disabledFullScreen) return
  60. uni.webView.postMessage({
  61. data: {
  62. action: 'fullscreen',
  63. data: e.detail
  64. }
  65. });
  66. }
  67. const handleRate = (rate) => {
  68. if (!videoCtx.value) return
  69. videoCtx.value.playbackRate(rate)
  70. currentRate.value = rate
  71. }
  72. const handleError = (e) => {
  73. if (!safeUrl.value) return // 此时不算异常
  74. console.log('mx-video error happens', e)
  75. emits("error", e)
  76. }
  77. </script>
  78. <style scoped>
  79. .rate-item {
  80. min-width: 42px;
  81. }
  82. ::v-deep .uv-tags {
  83. justify-content: center;
  84. }
  85. </style>