livingApp.nvue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. <template>
  2. <view>
  3. <view :style="'width: '+ windowWidth +'px; height: '+ boxStyle.height +'px;'">
  4. <list @scroll="scrolls" :show-scrollbar="false" ref="listBox" :pagingEnabled="true" :scrollable="true">
  5. <cell v-for="(item,i) in dataList" :key="i">
  6. <!-- 用div把视频模组套起来 -->
  7. <view :style="'width: '+ windowWidth +'px; height: '+ boxStyle.height +'px;'" :ref="'item'+i"
  8. style="position: relative;">
  9. <view v-if="(k-i)<=1" style="position: relative;">
  10. <view class="root">
  11. <video :id="item._id" :loop="true" :src="item.src"
  12. @play="playIngs(i)" :enable-progress-gesture="false" :page-gesture="false"
  13. :controls="false" :http-cache="true" :show-loading="false"
  14. :show-fullscreen-btn="false" :show-center-play-btn="false" :style="boxStyle"
  15. :object-fit="object_fit" @timeupdate="timeupdate($event,i)"
  16. :poster="item.src+'?x-oss-process=video/snapshot,t_480,f_jpg'"></video>
  17. <!-- 这里是封面 -->
  18. <!-- <image v-if="!item.playIng" :src="item.src+'?x-oss-process=video/snapshot,t_480,f_jpg'"
  19. :mode="mode"
  20. :style="'width: '+ windowWidth +'px; height: '+ (wHeight - deleteHeight) +'px; position: absolute;'">
  21. </image> -->
  22. </view>
  23. <!-- 这个是暂停时出现的图标 -->
  24. <view class="videoHover" @click="tapVideoHover(item.state,$event)"
  25. @touchstart="touchstartHover" :style="boxStyle">
  26. <image v-if="item.state=='pause' && item.isShowPlayIcon" class="playState" src="/static/image/course/play.png"></image>
  27. </view>
  28. <!-- 这里是直播时的界面其他内容 -->
  29. <view class="es es-ac es-h-88 " style="right:0;position: absolute;left:20rpx;" :style="'top:'+top+'px'">
  30. <image class="es-w-18 es-h-31 es-mr-20 es-ml-30" @tap="navBack()" src="/static/images/other/ret-white.png"></image>
  31. <view class="es-brc es-h-60 es es-ac es-br" style="background-color: rgba(255,255,255,0.2);">
  32. <view class="es-icon-57 es-br es-oh es-bc-red es es-ac es-pc">
  33. <image class="es-icon-57" style="border-radius: 29rpx;" src="/static/image/mine/doctor.png"></image>
  34. </view>
  35. <view class="es-c-white es-fw-500 es-fs-28 es-ml-11 es-mr-30" style="max-width: 6em;"><text class="es-c-white es-fs-28 es-fw-500">名字</text></view>
  36. <image class="es-icon-27 es-mr-21" src="/static/images/other/video/add.png"></image>
  37. </view>
  38. <view class="es-w-180"></view>
  39. <view class="es">
  40. <image class="es-br es-icon-57 es-ml-10" src="/static/image/mine/doctor.png"></image>
  41. <image class="es-br es-icon-57 es-ml-10" src="/static/image/mine/doctor.png"></image>
  42. <image class="es-br es-icon-57 es-ml-10" src="/static/image/mine/doctor.png"></image>
  43. </view>
  44. </view>
  45. <view class="person es es-ac " style="position: absolute;right:0;" :style="'top:'+top+'px'">
  46. <view class="num es es-ac es-pc "><text class="es-fs-28 es-fw-500 es-c-white">188人</text></view>
  47. </view>
  48. <view class="right es-c-white">
  49. <view class="item1 es es-ver es-ac es-pc es-mt-33" @tap="click()">
  50. <view class="es-icon-94 es-br-47 x-c" style="background-color:#ec5646;">
  51. <image class="es-icon-42" src="/static/images/other/video/like2.png"></image>
  52. </view>
  53. <view><text class="es-c-white es-fs-22 es-fw-500 es-mt-6">16.8万</text></view>
  54. </view>
  55. <view class="item1 es es-ver es-ac es-pc es-mt-33">
  56. <view class="es-icon-94 es-br-47 x-c" style="background-color:#5b6cde">
  57. <image class="es-icon-42" src="/static/images/other/video/collect2.png"></image>
  58. </view>
  59. <view><text class="es-c-white es-fs-22 es-fw-500 es-mt-6">3.2万</text></view>
  60. </view>
  61. <view class="item1 es es-ver es-ac es-pc es-mt-33">
  62. <view class="es-icon-94 es-br-47 x-c" style="background-color:#faa41d">
  63. <image class="es-icon-42" src="/static/images/other/video/star2.png"></image>
  64. </view>
  65. <view><text class="es-c-white es-fs-22 es-fw-500 es-mt-6">1.1万</text></view>
  66. </view>
  67. <view class="item1 es es-ver es-ac es-pc es-mt-33">
  68. <view class="es-icon-94 es-br-47 x-c" style="background-color:rgba(66,66,66,0.5)" >
  69. <image class="es-icon-42 es-h-14" src="/static/images/other/video/more.png"></image>
  70. </view>
  71. <view><text class="es-c-white es-fs-22 es-fw-500 es-mt-6">1.8万</text></view>
  72. </view>
  73. </view>
  74. <view class="bottom" style="width: 100%;">
  75. <view class="es-h-74 es es-ac">
  76. <view class="es-br-37 es-pl-20" :style="{width:(windowWidth-90)+'px'}" style="z-index: 1;width: 300rpx;background-color:rgba(66,66,66,0.5)">
  77. <input class="es-c-white es-fw-500 es-fs-26 es-h-74" type="text" placeholder="说点什么..."/>
  78. </view>
  79. <view class="es-icon-74 es-br-37 x-c es-ml-20" style="z-index: 1;background-color:#ff5000">
  80. <image class="es-icon-38" src="/static/images/other/video/car2.png"></image>
  81. </view>
  82. <view class="es-w-30"></view>
  83. </view>
  84. </view>
  85. <!-- <es-living-pop></es-living-pop> -->
  86. <!-- 这里是播放视频的其他内容 -->
  87. <!-- <es-video-pop></es-video-pop> -->
  88. </view>
  89. </view>
  90. </cell>
  91. </list>
  92. </view>
  93. </view>
  94. </template>
  95. <script>
  96. import userList from './data.js' //这个是假数据
  97. export default {
  98. data() {
  99. return {
  100. //下面打🌟号的是必须要的基础字段
  101. //下面打💗号的是拥有滑动条的必须字段
  102. dataList: [], //用于数据循环的列表🌟💗
  103. wHeight: 0, //获取的屏幕高度🌟💗
  104. boxStyle: { //视频,图片封面样式🌟💗
  105. 'height': 0,
  106. 'width': 0,
  107. },
  108. Heights: 0,
  109. k: 0, //默认为0🌟💗
  110. playIngIds: [], //正在播放的视频id列队,列队用于处理滑动过快导致的跳频问题🌟💗
  111. ready: false, //可忽略
  112. isDragging: false, //false代表停止滑动🌟💗
  113. refreshing: true, //用于下拉刷新🌟💗
  114. windowWidth: 0, //获取屏幕宽度🌟💗
  115. windowHeight: 0,
  116. dex: [0, 0], //用于判断是上滑还是下滑,第一个存旧值,第二个存新值【目前在1.0.7已经废弃】
  117. currents: 0, //用于左右滑动,0代表视频界面,1代表右滑界面🌟💗
  118. platform: '', //用于获取操作系统:ios、android🌟💗
  119. playIng: false, //用于视频初始化时是否播放,默认不播放🌟💗
  120. videoTime: '', //视频总时长,这个主要用来截取时间数值💗
  121. videoTimes: '', //视频时长,用这个来获取时间值,例如:00:30这个时间值💗
  122. changeTime: '', //显示滑动进度条时变化的时间💗
  123. isShowimage: false, //是否显示封面【1.0.4已废弃,但是意思需要记住】
  124. currenttimes: 0, //当前时间💗
  125. isShowProgressBarTime: false, //是否拖动进度条,如果拖动(true)则显示进度条时间,否则不显示(false)【1.0.4已废弃,但是意思需要记住】
  126. ProgressBarOpacity: 0.7, //进度条不拖动时的默认值,就是透明的💗
  127. dotWidth: 0, //播放的小圆点,默认没有💗
  128. deleteHeight: 0, //测试高度🌟💗
  129. percent: 0, //百分小数💗
  130. currentPosition: 0, //滑块当前位置💗//2.0已弃用,现已用于后端参数
  131. currentPositions: 0, //滑块当前位置的副本💗//2.0已弃用,现已用于后端参数
  132. newTime: 0, //跟手滑动后的最新时间💗
  133. timeNumber: 0, //🌟💗
  134. ProgressBarBottom: 20, //进度条离底部的距离💗
  135. object_fit: 'contain', //视频样式默认包含🌟💗
  136. mode: 'aspectFit', //图片封面样式🌟💗
  137. timeout: "", //🌟用来阻止 setTimeout()方法
  138. voice: "", //🌟用来阻止 setTimeout()方法
  139. oldVideo: "",
  140. isAutoplay: false, //是否开启自动播放(默认不开启)
  141. autoplayText: "开启自动播放",
  142. timers: "",
  143. // 引入评论 - 参数
  144. heightNum: 1.18,
  145. // 双击点赞参数
  146. touchNum: 0,
  147. aixinLeft: 0,
  148. aixinTop: 0,
  149. isShowAixin: false,
  150. Rotate: 0,
  151. isShow1: false, //控制渲染变量1
  152. isShow2: false, //控制渲染变量2 : 专门控制 uni-popup
  153. showPlay: false, //转轮显示控制
  154. rotates: 0, //转轮旋转角度
  155. rotateTime: "", //转轮递归事件控制
  156. xrotats: "",
  157. player: "",
  158. top:0,
  159. w:0,
  160. }
  161. },
  162. created: function() {
  163. setTimeout(e => {
  164. uni.createSelectorQuery().select('#w').boundingClientRect(r2 => {
  165. //this.w = r2.width;
  166. }).exec();
  167. }, 50)
  168. },
  169. onLoad(options) {
  170. this.windowWidth = uni.getSystemInfoSync().screenWidth //获取屏幕宽度
  171. this.boxStyle.width = this.windowWidth + 'px' //给宽度加px
  172. this.wHeight = uni.getSystemInfoSync().screenHeight; //获取屏幕高度
  173. this.boxStyle.height = this.wHeight - this.deleteHeight; //改变视频高度
  174. setTimeout(e=>{
  175. this.getData();
  176. },10)
  177. setTimeout(e=>{
  178. this.tapVideoHover("");
  179. },100)
  180. uni.getSystemInfo({
  181. success: (res) => {
  182. this.top = res.safeArea.top;
  183. }
  184. });
  185. },
  186. onShow(){
  187. console.log('回到前台');
  188. if(this.dataList.length !== 0){
  189. this.dataList[this.k].state = 'play';
  190. console.log('1');
  191. uni.createVideoContext(this.dataList[this.k]._id,this).play()
  192. console.log('2');
  193. }
  194. },
  195. onHide(){
  196. this.dataList[this.k].state = 'pause';//界面隐藏也要停止播放视频
  197. uni.createVideoContext(this.dataList[this.k]._id,this).pause();//暂停以后继续播放
  198. console.log('到后台');
  199. },
  200. watch:{
  201. k(k,old_k){//监听 k 值的变化,可以控制视频的播放与暂停
  202. console.log(k)
  203. // 清理定时器
  204. // this.dataList[old_k].state = 'stop'//如果是被滑走的视频,就停止播放
  205. this.dataList[old_k].playIng = false//如果视频暂停,就加载封面
  206. this.dataList[old_k].isplay = true
  207. uni.createVideoContext(this.dataList[old_k]._id,this).play()
  208. clearTimeout(this.oldVideo)
  209. this.oldVideo = setTimeout(()=>{
  210. uni.createVideoContext(this.dataList[old_k]._id,this).seek(0)
  211. uni.createVideoContext(this.dataList[old_k]._id,this).pause()
  212. console.log('预留第' + (old_k + 1) + '个视频:' + this.dataList[old_k]._id)
  213. },500)
  214. // 2.0版本已经去掉了下面这一句,视频不用暂停,只需要把声音禁止就行
  215. // uni.createVideoContext(this.dataList[old_k]._id + '' + old_k,this).stop()//如果视频暂停,那么旧视频停止,这里的this.dataList[old_k]._id + '' + old_k,后面加 old_k 是为了每一个视频的 id 值不同,这样就可以大程度的避免串音问题
  216. this.dataList[k].state = 'play'
  217. console.log('已经暂停 --> 第' + (old_k + 1) + '个视频~')//提示
  218. clearTimeout(this.player);
  219. this.player = setTimeout(()=>{
  220. uni.createVideoContext(this.dataList[k]._id,this).play();
  221. },50);
  222. if(k == (this.dataList.length-1)) {
  223. (async ()=>{
  224. await this.getData();
  225. // 【2.0版本更新内容】- start
  226. var p = k;
  227. ++p;
  228. this.dataList[p].isplay = true
  229. setTimeout(()=>{
  230. uni.createVideoContext(this.dataList[p]._id,this).play()
  231. clearTimeout(this.timeout)
  232. this.timeout = setTimeout(()=>{
  233. uni.createVideoContext(this.dataList[p]._id,this).seek(0)
  234. uni.createVideoContext(this.dataList[p]._id,this).pause()
  235. console.log('预加载第' + (p + 1) + '个视频:' + this.dataList[p]._id)
  236. },1500)
  237. },20)
  238. //【2.0版本更新内容】- end
  239. })();
  240. }
  241. //【此处处理进度条卡住的问题】
  242. if(uni.getSystemInfoSync().platform !== 'ios'){
  243. setTimeout(()=>{
  244. uni.createVideoContext(this.dataList[k]._id,this).pause()
  245. uni.createVideoContext(this.dataList[k]._id,this).play()
  246. },100)
  247. }
  248. this.xrotats = setTimeout(()=>{
  249. this.showPlay = true;
  250. },200)
  251. }
  252. },
  253. methods: {
  254. getData:function(){
  255. userList.forEach(e=>{
  256. e.id = 'A'+e.id+''+parseInt(Math.random()*1000);
  257. this.dataList.push(e);
  258. });
  259. },
  260. touchstart(event) {
  261. this.dataList[this.k].isShowimage = true //刚触摸的时候就要显示预览视频图片了
  262. this.dataList[this.k].isShowProgressBarTime = true //显示时间线
  263. this.ProgressBarOpacity = 1 //让滑块显示起来更明显一点
  264. this.dotWidth = 10 //让点显示起来更明显一点
  265. },
  266. touchend() { //当手松开后,跳到最新时间
  267. console.log('touchEnd');
  268. uni.createVideoContext(this.dataList[this.k]._id, this).seek(this.newTime)
  269. if (this.dataList[this.k].state == 'pause') {
  270. this.dataList[this.k].state = 'play'
  271. uni.createVideoContext(this.dataList[this.k]._id, this).play();
  272. console.log('touchEnd 播放数据',this.k,this.dataList[this.k]._id);
  273. }
  274. this.dataList[this.k].isShowProgressBarTime = false //触摸结束后,隐藏时间线
  275. this.dataList[this.k].isShowimage = false //触摸结束后,隐藏时间预览
  276. this.ProgressBarOpacity = 0.5 //隐藏起来进度条,不那么明显了
  277. this.dotWidth = 0 //隐藏起来进度条,不那么明显了
  278. },
  279. touchmove(event) { //当手移动滑块时,计算位置、百分小数、新的时间
  280. var msg = []
  281. if (this.videoTime !== '') {
  282. msg = this.videoTime.split(':')
  283. }
  284. var timeNumber = Number(msg[0]) * 60 + Number(msg[1])
  285. this.currentPositions = event.changedTouches[0].screenX
  286. this.percent = this.currentPositions / this.windowWidth
  287. this.newTime = this.percent * timeNumber
  288. this.currenttimes = parseInt(this.newTime)
  289. let theTime = this.newTime
  290. let middle = 0; // 分
  291. if (theTime > 60) {
  292. middle = parseInt(theTime / 60);
  293. theTime = parseInt(theTime % 60);
  294. }
  295. this.changeTime = `${Math.round(middle)>9?Math.round(middle):'0'+Math.round(middle)}:${Math.round(theTime)>9?Math.round(theTime):'0'+Math.round(theTime)}`
  296. },
  297. tapVideoHover(state,event){
  298. console.log('kkkk=>',this.k);
  299. this.dataList[this.k].isShowimage = false
  300. this.dataList[this.k].isShowProgressBarTime = false
  301. this.ProgressBarOpacity = 0.5
  302. this.dotWidth = 0
  303. console.log('state--',state);
  304. // 1.启用双击点赞 --- start
  305. if(state=='play'||state=='continue'){
  306. this.dataList[this.k].state = 'pause';
  307. }else{
  308. this.dataList[this.k].state = 'continue';
  309. }
  310. this.dataList[this.k].isShowPlayIcon = true;
  311. console.log('xxx',this.dataList[this.k].state);
  312. if(this.dataList[this.k].state == 'continue'){
  313. console.log('播放数据',this.dataList[this.k]._id);
  314. uni.createVideoContext(this.dataList[this.k]._id,this).play();//暂停以后继续播放
  315. }
  316. if(this.dataList[this.k].state == 'pause'){
  317. uni.createVideoContext(this.dataList[this.k]._id,this).pause();//暂停以后继续播放
  318. }
  319. },
  320. scrolls (event) {
  321. console.log('scrolls');
  322. this.isDragging = event.isDragging;
  323. if (!event.isDragging) {//isDragging:判断用户是不是在滑动,滑动:true,停止滑动:false。我们要用户停止滑动时才给 k 赋值,这样就可以避免很多麻烦
  324. var i = Math.round(Math.abs(event.contentOffset.y) / (this.wHeight - this.deleteHeight + 1))//先用绝对值取出滑动的距离,然后除以屏幕高度,取一个整,就知道你现在滑动到哪一个视频了
  325. if(i !== this.k){//这里加判断是因为这个方法会执行很多次,会造成重复请求,所以这里写一个限制
  326. let num = 300;
  327. clearTimeout(this.timers);
  328. this.timers = setTimeout(()=>{
  329. this.k = i;//判断了用户没有滑动,确认了用户的确是在看这个视频,然后就赋值啦
  330. this.dataList[this.k].state = 'play';
  331. console.log('正在播放 --> 第' + (this.k + 1) + '个视频~');
  332. },num);
  333. }
  334. }
  335. },
  336. timeupdate:function(){
  337. },
  338. playIngs:function(){
  339. },
  340. click:function(){
  341. console.log('click');
  342. },
  343. navBack:function(){
  344. uni.navigateBack({
  345. animationType: 'pop-out',
  346. animationDuration: 200
  347. });
  348. },
  349. }
  350. }
  351. </script>
  352. <style>
  353. .container {
  354. background-color: #000000;
  355. }
  356. .item {
  357. /* width : 750rpx; */
  358. background-color: #000000;
  359. position: relative;
  360. }
  361. .videoHover {
  362. position: absolute;
  363. top: 0;
  364. left: 0;
  365. flex: 1;
  366. background-color: rgba(0, 0, 0, 0.1);
  367. justify-content: center;
  368. align-items: center;
  369. /* border-style: dashed;
  370. border-color: #DD524D;
  371. border-width: 1px; */
  372. }
  373. .playState {
  374. width: 80rpx;
  375. height: 80rpx;
  376. opacity: 0.9;
  377. }
  378. .userInfo {
  379. position: absolute;
  380. bottom: 80px;
  381. right: 10px;
  382. flex-direction: column;
  383. }
  384. .userAvatar {
  385. border-radius: 500%;
  386. margin-bottom: 15px;
  387. border-style: solid;
  388. border-width: 2px;
  389. border-color: #ffffff;
  390. }
  391. .userAvatar {
  392. width: 100rpx;
  393. height: 100rpx;
  394. }
  395. .likeIco,
  396. .commentIco
  397. .shareIco{
  398. width: 60rpx;
  399. height: 60rpx;
  400. margin-top: 15px;
  401. }
  402. .likeNum,
  403. .commentNum,
  404. .shareTex {
  405. color: #ffffff;
  406. font-size: 30rpx;
  407. text-align: center;
  408. margin: 5px;
  409. }
  410. .likeNumActive {
  411. color: red;
  412. }
  413. .content {
  414. width: 610rpx;
  415. z-index: 99;
  416. position: absolute;
  417. bottom: 30px;
  418. /* background-color: #007AFF; */
  419. /* justify-content: center; */
  420. padding: 15rpx;
  421. flex-direction: column;
  422. justify-content: flex-start;
  423. color: #ffffff;
  424. }
  425. .userName {
  426. font-size: 30rpx;
  427. color: #ffffff;
  428. margin-top: 80upx;
  429. }
  430. .words {
  431. margin-top: 10rpx;
  432. font-size: 30rpx;
  433. color: #ffffff;
  434. }
  435. .root {
  436. /* background-color: #000000; */
  437. background-color: #00ffff;
  438. }
  439. .person {
  440. position: absolute;
  441. right: 0;
  442. height: 88rpx;
  443. }
  444. .person .num {
  445. background-color: rgba(255, 100, 3, 1);
  446. height: 58rpx;
  447. border-radius: 100rpx 0 0 100rpx;
  448. padding: 0 20rpx;
  449. }
  450. .right,
  451. .bottom {
  452. position: absolute;
  453. bottom: 80rpx;
  454. padding-bottom: env(safe-area-inset-bottom);
  455. }
  456. .right {
  457. right: 30rpx;
  458. bottom: 250rpx;
  459. }
  460. .right .item1 {
  461. width: 100rpx;
  462. text-align: center;
  463. }
  464. .right .iconbox{
  465. width: 94rpx;
  466. }
  467. .right image {
  468. width: 100%;
  469. height: 100%;
  470. }
  471. .bottom {
  472. left: 30rpx;
  473. }
  474. .goods .icon,
  475. .goods .icon image {
  476. width: 290rpx;
  477. height: 290rpx;
  478. }
  479. .bg {
  480. /* background-image: url(/static/images/other/video/bg.png); */
  481. position: absolute;
  482. left: 0;
  483. top: 0;
  484. width: 100%;
  485. height: 74rpx;
  486. }
  487. .goods {
  488. width: 330rpx;
  489. background-color: rgba(255, 255, 255, 0.4);
  490. padding: 20rpx;
  491. border-radius: 20rpx;
  492. margin-top: 20rpx;
  493. }
  494. </style>