swiper-vertical-video.uvue 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. <template>
  2. <view class="page">
  3. <view ref="back" @click="back" class="nav-back">
  4. <image class="back-img" src="/static/template/scroll-fold-nav/back.png" mode="widthFix"></image>
  5. </view>
  6. <swiper class="swiper" :current="current" :circular="index != 0" :vertical="true" @change="onSwiperChange"
  7. @transition="onTransition">
  8. <swiper-item class="swiper-item" v-for="(item,i) in visibleList" :key="i">
  9. <video @click="changeState(i)" ref="video" class="video-box" :id="'video-'+i"
  10. @loadstart="onLoadstart(i)" :src="item.src" :poster="item.poster_src" :autoplay="false"
  11. :show-progress="false" :show-fullscreen-btn="false" :show-play-btn="false"
  12. :show-center-play-btn="false" :loop="true" @play="onPlay(i)" @pause="onPause(i)" http-cache="true"></video>
  13. <view class="video-cover" @click="changeState(i)">
  14. <image v-if="state[i] === 'pause'" class="play-btn" src="/static/template/swiper-vertical-video/play.png"
  15. mode="widthFix"></image>
  16. </view>
  17. <view class="video-info" v-if="showDebugInfo">
  18. <text class="video-info-text">容器:第 {{i}} 个</text>
  19. <text class="video-info-text">内容:{{item.content}}</text>
  20. </view>
  21. </swiper-item>
  22. </swiper>
  23. <view class="debug-info" v-if="showDebugInfo">
  24. <text class="status-text">debug-info 播放状态:</text>
  25. <text class="status-text" v-for="(value,index) in state">第{{index+1}}个:{{ value }}</text>
  26. </view>
  27. </view>
  28. </template>
  29. <script>
  30. type ListItem = { _id : string, content : string, src : string, poster_src : string }
  31. let page : number = 0,
  32. currentPageIsShow : boolean = true;
  33. import { state, setNetless } from '@/store/index.uts'
  34. export default {
  35. components: {},
  36. data() {
  37. return {
  38. $videoContextMap: new Map<string, VideoContext>(),
  39. list: [] as ListItem[],
  40. visibleList: [] as ListItem[],// 提高性能 可见的只有3个
  41. current: 0 as number,
  42. index: 0,
  43. state: ["pause", "unPlay", "unPlay"] as string[],
  44. showDebugInfo: false as boolean
  45. }
  46. },
  47. created() {
  48. // #ifdef APP-HARMONY
  49. uni.getNetworkType({
  50. success: (res) => {
  51. setNetless(res.networkType === 'none')
  52. // #endif
  53. this.list = this.getData()
  54. this.visibleList = this.list.slice(0, 3)
  55. // #ifdef APP-HARMONY
  56. }
  57. })
  58. // #endif
  59. },
  60. watch: {
  61. current(current : number, oldCurrent : number) {
  62. let changeNumber = current - oldCurrent
  63. if (changeNumber == 1 || changeNumber == -2) {
  64. // console.error('向右');
  65. this.index++
  66. } else {
  67. // console.error('向左');
  68. this.index--
  69. }
  70. // //翻页(3项为一页)
  71. if (Math.abs(changeNumber) == 2) {
  72. // console.log('翻页');
  73. page = Math.floor(this.index / 3);
  74. // console.log(this.index);
  75. // console.log('page',page);
  76. // console.log('slice',3*page,3*page+3);
  77. if (this.list.length < 3 * page + 3) {
  78. let list : ListItem[] = this.getData()
  79. this.list.push(...list)
  80. }
  81. let visibleList = this.list.slice(3 * page, 3 * page + 3)
  82. // 换数据
  83. this.visibleList = visibleList
  84. }
  85. this.state.forEach((_ : string, index : number) => {
  86. if (index === current) {
  87. this.doPlay(current)
  88. } else {
  89. // 除了选中的其他已经播放的都需要停止
  90. this.doStop(index)
  91. console.log('index:' + index + '已被执行停止');
  92. }
  93. })
  94. }
  95. },
  96. onReady() {
  97. // #ifdef APP || MP
  98. // 非web端,一启动完成,就播放第一个。因为 web端浏览器限制不支持自动播放
  99. this.doPlay(0);
  100. //App端适配不同状态栏高度
  101. let backElement = this.$refs['back'] as UniElement;
  102. backElement.style.setProperty('top', uni.getWindowInfo().statusBarHeight+'px');
  103. // #endif
  104. },
  105. onShow() {
  106. currentPageIsShow = true
  107. },
  108. onHide() {
  109. currentPageIsShow = false
  110. console.log('pages-onHide');
  111. this.doPause(this.current)
  112. },
  113. onUnload() {
  114. this.doPause(this.current)
  115. },
  116. methods: {
  117. changeState(index : number) {
  118. if (this.state[index] === 'play') {
  119. this.doPause(index)
  120. } else {
  121. this.doPlay(this.current)
  122. }
  123. },
  124. onLoadstart(index : number) {
  125. console.error("onLoadstart video" + index);
  126. },
  127. getVideoContext(index : number) : VideoContext {
  128. const videoContextMap = this.$data['$videoContextMap'] as Map<string, VideoContext>
  129. let videoContext : VideoContext | null = videoContextMap.get('video-' + index)
  130. if (videoContext == null) {
  131. videoContext = uni.createVideoContext('video-' + index, this) as VideoContext
  132. videoContextMap.set('video-' + index, videoContext)
  133. }
  134. return videoContext
  135. },
  136. doPlay(index : number) {
  137. console.log("doPlay video" + index);
  138. this.getVideoContext(index).play()
  139. },
  140. doStop(index : number) {
  141. console.log("doStop video-" + index);
  142. this.getVideoContext(index).stop();
  143. // TODO 临时方案:解决.stop()时触发了doPause的问题
  144. setTimeout(() => {
  145. this.state[index] = 'unPlay'
  146. }, 1000)
  147. },
  148. doPause(index : number) {
  149. this.getVideoContext(index).pause()
  150. console.log("doPause video-" + index);
  151. },
  152. onPause(index : number) {
  153. this.state[index] = 'pause'
  154. console.log('onPause', index);
  155. },
  156. onPlay(index : number) {
  157. if (this.current != index || !currentPageIsShow) {
  158. this.onPause(index)
  159. } else {
  160. this.state[index] = 'play'
  161. console.log('onPlay', index);
  162. }
  163. },
  164. getData() : ListItem[] {
  165. let videoUrlList = state.netless
  166. ? [
  167. '/static/test-video/10second-demo.mp4',
  168. '/static/test-video/10second-demo.mp4',
  169. '/static/test-video/10second-demo.mp4'
  170. ]
  171. : [
  172. 'https://web-ext-storage.dcloud.net.cn/uni-app-x/video/uts-5-16.mp4',
  173. 'https://web-ext-storage.dcloud.net.cn/uni-app-x/video/uni-ai-5-16.mp4',
  174. 'https://web-ext-storage.dcloud.net.cn/uni-app-x/video/uni-verify-5-16.mp4'
  175. ] as string[]
  176. let posterSrcList = state.netless
  177. ? [
  178. '/static/shuijiao.jpg',
  179. '/static/logo.jpg',
  180. '/static/shuijiao.jpg'
  181. ]
  182. : [
  183. 'https://web-ext-storage.dcloud.net.cn/uni-app-x/dark-uni-uts-01.png',
  184. 'https://web-ext-storage.dcloud.net.cn/uni-app-x/dark-uni-ai-01.png',
  185. 'https://web-ext-storage.dcloud.net.cn/uni-app-x/dark-uni-verify-01.jpg'
  186. ] as string[]
  187. let list = [] as ListItem[];
  188. for (let i = 0; i < 6; i++) {
  189. let index = this.list.length + i;
  190. let listItem : ListItem = {
  191. "_id": "a00" + index,
  192. "content": "这是第" + index + "条数据,url地址" + videoUrlList[i % 3],
  193. "src": videoUrlList[i % 3],
  194. "poster_src": posterSrcList[i % 3]
  195. }
  196. list.push(listItem)
  197. }
  198. return list
  199. },
  200. onSwiperChange(e : SwiperChangeEvent) {
  201. // console.error('SwiperChangeEvent',e.detail.current);
  202. this.current = e.detail.current;
  203. },
  204. onTransition(/*e : SwiperTransitionEvent*/) {
  205. // console.log('onTransition e.detail.dx', e.detail.dx);
  206. },
  207. back() {
  208. uni.navigateBack({
  209. success(result) {
  210. console.log('navigateBack success', result.errMsg)
  211. },
  212. fail(error) {
  213. console.log('navigateBack fail', error.errMsg)
  214. },
  215. complete(result) {
  216. console.log('navigateBack complete', result.errMsg)
  217. }
  218. })
  219. }
  220. }
  221. }
  222. </script>
  223. <style>
  224. .page {
  225. flex: 1;
  226. }
  227. .swiper,
  228. .swiper-item,
  229. .video-box,
  230. .video-cover {
  231. width: 100%;
  232. height: 100%;
  233. }
  234. .swiper-item {
  235. position: relative;
  236. }
  237. .video-box {
  238. width: 100%;
  239. }
  240. .video-cover {
  241. position: absolute;
  242. justify-content: center;
  243. align-items: center;
  244. align-content: center;
  245. }
  246. .play-btn {
  247. width: 40px;
  248. height: 40px;
  249. }
  250. .video-info {
  251. position: absolute;
  252. bottom: 0;
  253. padding: 15px;
  254. }
  255. .video-info-text {
  256. font-size: 14px;
  257. color: red;
  258. line-height: 20px;
  259. }
  260. .debug-info {
  261. position: fixed;
  262. top: 15px;
  263. width: 100%;
  264. background-color: rgba(255, 255, 255, 0.3);
  265. }
  266. .status-text {
  267. color: red;
  268. padding: 15px;
  269. }
  270. .nav-back {
  271. position: absolute;
  272. /* #ifdef APP */
  273. top: 35px;
  274. /* #endif */
  275. background-color: rgba(220, 220, 220, 0.8);
  276. border-radius: 100px;
  277. margin: 6px;
  278. width: 32px;
  279. height: 32px;
  280. justify-content: center;
  281. align-items: center;
  282. z-index: 10;
  283. }
  284. .nav-back .back-img {
  285. width: 18px;
  286. height: 18px;
  287. }
  288. </style>