y-video copy 2.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. <template>
  2. <view>
  3. <DomVideoPlayer class="video" :style="{ 'height': height + 'px' }" :ref="`domVideoPlayer_${current}`" object-fit='contain'
  4. :controls="controls" :autoplay="autoplay" :loop="loop" :src="item.src" :playback-rate="playbackRate"
  5. @play="onPlay" @pause="onPause" @ended="onEnded" @timeupdate="onTimeUpdate" @durationchange="onDurationChange"
  6. @ratechange="onRateChange" @fullscreenchange="onFullscreenChange" @tap.stop="change" />
  7. <view class="play-btn" :style="{ left: `${width / 2}px`, top: `${height / 2}px` }" v-if="showPlayBtn">
  8. <up-icon name="play-right-fill" size="16" color="var(--light)"></up-icon>
  9. </view>
  10. <!-- loading -->
  11. <image class="loading" :style="{ left: `${width / 2}px`, top: `${height / 2}px` }" src="@/static/loading.gif"
  12. v-if="loading">
  13. </image>
  14. <view class="info" @click.stop="$emit('showMulu')">
  15. <text class="text title">@{{ item.title }}</text>
  16. <text class="text" style="margin-top: 25rpx;font-size: 26rpx;line-height: 40rpx;">{{ item.desc ||
  17. '讲述温暖出行故事,描绘人间烟火,大锁与陈珂从原来的两条“平行线”,变成了感情不断升温的情侣...'}}</text>
  18. </view>
  19. <view class="btns" :style="{ height: height + 'px' }" @click.stop>
  20. <image class="toux" style="margin-bottom: 40rpx;" src="@/static/tx.png"></image>
  21. <image class="btn" :src="isZan ? '/static/dianzan_h.png' : '/static/dianzan.png'" @click="isZan = !isZan">
  22. </image>
  23. <text class="text">0</text>
  24. <image class="btn" src="@/static/pinglun.png" @click.stop="emit('showPinglun')"></image>
  25. <text class="text">0</text>
  26. <image class="btn" :src="isSc ? '/static/shoucang_h.png' : '/static/shoucang.png'" @click="isSc = !isSc">
  27. </image>
  28. <text class="text">0</text>
  29. <image class="btn" src="@/static/fenxiang.png?v=6"></image>
  30. </view>
  31. <view class="progress">
  32. <view class="line" :style="{ width: lineWidth + 'px' }"></view>
  33. </view>
  34. <view class="_duration" @click.stop>
  35. <view style="flex-direction: row;flex: 1;">
  36. <text class="text">{{ currentTime ? currentTime : '00:00' }}</text>
  37. <text class="text" style="margin: 0 10rpx;">/</text>
  38. <text class="text" style="opacity: .5;">{{ duration ? duration : '00:00' }}</text>
  39. </view>
  40. <image class="shengyin" :src="muted ? '/static/jingyin.png' : '/static/yinliang.png'" @click="muted = !muted">
  41. </image>
  42. <image class="shengyin" style="margin-left:20rpx" src="/static/quanping.png" @click="fullScreen">
  43. </image>
  44. </view>
  45. </view>
  46. </template>
  47. <script setup>
  48. import { ref, computed,onUnmounted } from 'vue'
  49. import DomVideoPlayer from '@/components/DomVideoPlayer'
  50. import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";
  51. const systemInfo = uni.getSystemInfoSync();
  52. const width = systemInfo.screenWidth;
  53. const height = systemInfo.screenHeight - 50;
  54. // 将xx秒转为 xx:xx 分秒格式
  55. const formatSec2Time = (time) => {
  56. const min = Math.floor(time / 60)
  57. const sec = Math.floor(time % 60)
  58. return `${min}:${sec < 10 ? '0' + sec : sec}`
  59. }
  60. const props = defineProps({
  61. info: {
  62. type: Object,
  63. default: () => { }
  64. },
  65. item: {
  66. type: Object,
  67. default: () => { }
  68. },
  69. index: {
  70. type: Number,
  71. default: -1
  72. },
  73. current: {
  74. type: Number,
  75. default: 0
  76. }
  77. })
  78. const playing = ref(true)
  79. const loop = ref(true)
  80. const controls = ref(false)
  81. const autoplay = ref(true)
  82. const playbackRate = ref(1)
  83. const currentTime = ref(0)
  84. const duration = ref(0)
  85. const domVideoPlayer = ref(null);
  86. const showPlayBtn = ref(false);
  87. const loading = ref(false);
  88. const lineWidth = ref(0);
  89. const muted = ref(false);
  90. const isZan = ref(false);
  91. const isSc = ref(false);
  92. const isFullscreen = ref(false);
  93. const change = () => {
  94. if (!isFullscreen.value) {
  95. playing.value ? pause() : play()
  96. }
  97. }
  98. const play = () => {
  99. showPlayBtn.value = false
  100. playing.value = true
  101. domVideoPlayer.value.play()
  102. }
  103. const pause = () => {
  104. showPlayBtn.value = true
  105. playing.value = false
  106. domVideoPlayer.value.pause()
  107. }
  108. const onPlay = () => {
  109. console.log('onPlay')
  110. if (props.current != props.index) {
  111. domVideoPlayer.value.pause()
  112. playing.value = false
  113. } else {
  114. playing.value = true
  115. showPlayBtn.value = false
  116. loading.value = false
  117. }
  118. }
  119. const onPause = () => {
  120. console.log('onPause')
  121. showPlayBtn.value = true
  122. playing.value = false
  123. }
  124. const onEnded = () => {
  125. console.log('onEnded')
  126. playing.value = false
  127. }
  128. const fullScreen = () => {
  129. console.log('全屏');
  130. domVideoPlayer.value.fullScreen();
  131. }
  132. const onDurationChange = (e) => {
  133. console.log(e);
  134. if (loading.value) loading.value = false;
  135. if (!playing.value) playing.value = true;
  136. if (!duration.value || duration.value == '00:00') duration.value = secondToTime(e, 1)
  137. }
  138. const onTimeUpdate = (e) => {
  139. currentTime.value = secondToTime(e)
  140. lineWidth.value = e / duration.value * width
  141. }
  142. const secondToTime = (s, n) => {
  143. const t = parseInt(s)
  144. const hours = Math.floor(t / (60 * 60));
  145. const seconds = t % (60 * 60)
  146. const minutes = Math.floor(seconds / 60);
  147. const seconds2 = seconds % 60
  148. if (hours > 0) {
  149. return `${hours}:${minutes.toString().padStart(2, '0')}:${seconds2.toString().padStart(2, '0')}`;
  150. } else {
  151. return `${minutes.toString().padStart(2, '0')}:${seconds2.toString().padStart(2, '0')}`;
  152. }
  153. }
  154. const onRateChange = (e) => {
  155. console.log('onRateChange', e)
  156. playbackRate.value = e
  157. }
  158. const onFullscreenChange = (e) => {
  159. console.log('onFullScreenChange', e)
  160. }
  161. // defineExpose({
  162. // play
  163. // })
  164. onUnmounted(()=>{
  165. console.log('组件卸载');
  166. })
  167. onHide(() => {
  168. pause()
  169. })
  170. </script>
  171. <style scoped lang="less">
  172. @import url('@/style.less');
  173. .action-box {
  174. margin-top: 30rpx;
  175. padding: 0 60rpx;
  176. }
  177. .action-box button {
  178. margin-top: 10rpx;
  179. }
  180. .video {
  181. width: 750rpx;
  182. background-color: #000000;
  183. }
  184. .text {
  185. color: #fff;
  186. font-size: 28rpx;
  187. }
  188. .info {
  189. position: absolute;
  190. z-index: 100;
  191. left: 0;
  192. bottom: 50px;
  193. width: 750rpx;
  194. padding: 30rpx 140rpx 130rpx 30rpx;
  195. .flex();
  196. flex-direction: column;
  197. .title {
  198. font-size: 36rpx;
  199. max-width: calc(750rpx - 140rpx - 60rpx);
  200. }
  201. .des {
  202. background-color: rgba(255, 255, 255, .1);
  203. border-radius: 10rpx;
  204. padding: 15rpx 20rpx;
  205. .text {
  206. font-size: 24rpx;
  207. }
  208. }
  209. }
  210. .btns {
  211. position: absolute;
  212. z-index: 100;
  213. right: 0;
  214. bottom: 0;
  215. width: 140rpx;
  216. .flex_position(flex-end);
  217. flex-direction: column;
  218. padding-bottom: 340rpx;
  219. .toux {
  220. height: 88rpx;
  221. width: 88rpx;
  222. border-radius: 50%;
  223. object-fit: cover;
  224. }
  225. .btn {
  226. height: 66rpx;
  227. width: 66rpx;
  228. }
  229. .text {
  230. margin-top: 10rpx;
  231. margin-bottom: 30rpx;
  232. font-size: 26rpx;
  233. }
  234. }
  235. .play-btn {
  236. height: 68rpx;
  237. width: 68rpx;
  238. border-radius: 50%;
  239. background-color: rgba(0, 0, 0, .5);
  240. position: absolute;
  241. margin-left: -34rpx;
  242. margin-top: -34rpx;
  243. .flex_center();
  244. }
  245. .loading {
  246. position: absolute;
  247. height: 60rpx;
  248. width: 60rpx;
  249. margin-left: -30rpx;
  250. margin-top: -30rpx;
  251. }
  252. .progress {
  253. width: 750rpx;
  254. height: 90rpx;
  255. position: absolute;
  256. left: 0;
  257. bottom: 50px;
  258. .line {
  259. height: 2rpx;
  260. width: 500rpx;
  261. background-color: rgba(255, 255, 255, .35);
  262. transform: scaleY(.1);
  263. }
  264. }
  265. ._duration {
  266. position: absolute;
  267. height: 88rpx;
  268. .ver();
  269. left: 0;
  270. width: 750rpx;
  271. bottom: 50px;
  272. z-index: 101;
  273. padding-left: 30rpx;
  274. padding-right: 30rpx;
  275. background-color: rgba(255, 255, 255, .05);
  276. }
  277. .shengyin {
  278. height: 38rpx;
  279. width: 38rpx;
  280. }
  281. </style>