uniapp 小程序实现类似抖音的简易刷视频功能
- 先上视频
20240725-163843
-
直接上代码
javascript
<template>
<view>
<view class="uni-padding-wrap">
<view class="page-section swiper">
<view class="page-section-spacing">
<swiper class="swiper" @change="changefun" @animationfinish="animationfinishfun" :current="index_"
:vertical="true">
<swiper-item v-for="(item,index) in videoList">
<view class="swiper-item" @click.stop="playPause(index)">
<image src="https://xxxxxxxxxxxxxx.com/2021083111362225433.png" v-if="!isPlay" class="btn play">
</image>
<image src="https://xxxxxxxxxxxxxx.com/2021083111373735978.png" v-if="isPlay&&firstclick"
class="btn pause"></image>
<video :custom-cache="false" loop="true" class="video" :id="'id'+index" :duration="item.duration"
:enable-progress-gesture="true" :controls="false" @timeupdate="(e)=>videoUpdate(e,item,index)"
:src="item.vlogUrl" :show-center-play-btn="false">
</video>
</view>
</swiper-item>
</swiper>
</view>
</view>
<tab-bar></tab-bar>
</view>
<view v-if="is_active">
<view class="left">
<view class="left_box">
<!-- #ifdef MP-ALIPAY -->
<view>
<!-- #endif -->
<view class="slider">
<slider v-model="activeObj.currentTime" @change="sliderChange" @changing="sliderChanging"
:activeColor="!isPlay || !updateState ?'#ffffff':'#ffffff4D'"
:block-size="!isPlay || !updateState?14:12" :block-color="!isPlay|| !updateState?'#ffffff':'#6e6f71'"
backgroundColor="#4f5253"></slider>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import tabBar from "../../../components/tabBar/tabBar";
export default {
components: {
tabBar
},
data() {
return {
videoList: [],
index_: null,
index: '0',
firstclick: false,
is_active: true,
isPlay: true,
activeObj: null,
sending: false,
contentId: '',
updateState: true,
videoContext: null,
}
},
onLoad(option) {
// 此接口用来查第一个视频详细信息
// this.$uniApi.dataRequestNoLoading('GET', 'base/app/v1/contentApp/getDefaultVlogId').then(res => {
// 我在此处替换为假数据
let res = {
"code": 1,
"data": {
"id": "1806517903331479553",
"title": "一日游",
"subtitle": null,
"coverImage": "https://xxxxxxxxxxxxxx.com/171954161525350063.jpg", // 请更换为自己的视频
"readingSum": "239",
"rankingInclude": null,
"includeContain": null,
"vlogUrl": "https://xxxxxxxxxxxxxx.com/2024062810390955101.mp4", // 请更换为自己的图片
"type": 9,
"salesNum": null,
"marketPrice": null,
"coverImageHeight": 702,
"coverImageWeight": 1080,
"headImgUrl": "https://xxxxxxxxxxxxxx.com/171954159983539103.png", // 请更换为自己的图片
"author": "一日游",
"isTopping": "0"
},
"msg": "成功",
"success": true
}
let id = res.data.id || ""
this.contentId = id
let obj = Object.assign({}, {
id: id
})
obj['istrue'] = false
obj['contentId'] = id
obj['currentTime'] = 0
// 用来计算滚动条
obj['duration'] = 0
// 视频链接
obj['vlogUrl'] = res.data.vlogUrl
//
this.videoList.push(obj)
// 查找视频列表 我的接口是根据当前的视频 id 去查后面的视频
// 一次查的太多会导致卡顿
this.oprateVideoList(this.contentId)
// 初始化第一个视频 并播放
this.$nextTick(function() {
let videoContext = uni.createVideoContext(`id0`)
videoContext.play()
})
// })
},
watch: {
index_(val) {
this.videoContext = uni.createVideoContext(`id${this.index_}`)
this.contentId = this.videoList[val]['contentId']
},
activeObj(val, oldval) {
if (!oldval) {
this.$nextTick(function() {
let videoContext = uni.createVideoContext(`id${this.index_}`)
videoContext.play()
})
}
}
},
methods: {
// current 改变时会触发 change 事件,event.detail = {current: current, source: source}
// 暂停当前的视频播放
changefun(e) {
this.is_active = false
let videoContext = uni.createVideoContext('id' + this.index_)
videoContext.pause()
},
playPause(current) {
// 点击视频时 如果播放就暂停 如果是暂停就开始播放
let videoContext = uni.createVideoContext('id' + current)
this.firstclick = true
if (this.isPlay) {
videoContext.pause()
this.isPlay = false
} else {
videoContext.play()
this.isPlay = true
}
},
// 动画结束时会触发 animationfinish 事件,event.detail = {current: current, source: source}
animationfinishfun(e) {
let that = this
this.isPlay = true
// 获取下标
let current = e.detail.current
this.activeObj = this.videoList[current]
this.index_ = current
this.is_active = true
this.videoList[current]['istrue'] = true
// 获取当前的 video 标签 并暂停播放
let videoContext = uni.createVideoContext('id' + this.index_)
videoContext.pause()
// 初始化新的video 并播放
videoContext = uni.createVideoContext('id' + current)
videoContext.play()
// 视频滑到最后一个 就再去请求新的 视频列表
if (this.videoList.length > 0 && current + 1 == this.videoList.length && !this.sending) {
this.oprateVideoList(this.videoList[current]['contentId'])
}
},
videoUpdate(e, item, index) {
// 此处用来计算进度条
if (this.updateState) {
// #ifdef MP-WEIXIN
if (e.target.id == 'id' + index) {
this.videoList[this.index_]['currentTime'] = e.detail.currentTime / e.detail.duration * 100
this.videoList[this.index_]['duration'] = e.detail.duration
}
// #endif
// #ifdef MP-ALIPAY
this.videoList[this.index_]['currentTime'] = e.detail.currentTime / e.detail.videoDuration * 100
this.videoList[this.index_]['duration'] = e.detail.videoDuration
// #endif
}
},
// 拖动过程中触发的事件,event.detail = {value: value}
sliderChanging(e) {
this.updateState = false
},
//拖动进度条触发事件
sliderChange(e) {
let duration = this.videoList[this.index_]['duration']
if (duration) {
this.videoContext.seek(e.detail.value / 100 * duration); //完成拖动后,计算对应时间并跳转到指定位置
this.videoList[this.index_]['duration'] = e.detail.value
this.updateState = true
}
},
// 获取视频列表
oprateVideoList(id) {
let that = this
this.sending = true
// this.$uniApi.dataRequestNoLoading('GET', 'base/app/v1/contentVlog/getVlogListByContentId', {
// contentId: id,
// }).then(resp => {
let resp = {
"code": 1,
"data": [{
"contentId": "1606207777364082690",
"title": "vlog啊",
"coverImage": "https://xxxxxxxxxxxxxx.com/167178470537619117.png",
"vlogUrl": "https://xxxxxxxxxxxxxx.com/2022122316383441583.mp4"
}, {
"contentId": "1684011338528985090",
"title": "vlog",
"coverImage": "https://xxxxxxxxxxxxxx.com/169033454163171300.png",
"vlogUrl": "https://xxxxxxxxxxxxxx.com/2023072609222991294.mp4"
}, {
"contentId": "1678315077972848642",
"title": "阿达啊",
"coverImage": "https://xxxxxxxxxxxxxx.com/168897636035107597.png",
"vlogUrl": "https://xxxxxxxxxxxxxx.com/2023071016074361328.mp4"
}],
"msg": "成功",
"success": true
}
resp.data.map(res => {
res['istrue'] = false
res['currentTime'] = 0
res['duration'] = 0
// return res
})
if (that.index_ === null) {
that.index_ = 0
}
that.videoList = that.videoList.concat(resp.data)
that.activeObj = that.videoList[that.index_]
that.sending = false
that.videoList[0]['istrue'] = true
// })
}
},
}
</script>
<style>
page {
background: #000;
}
</style>
<style scoped lang="scss">
.circle {
background: rgba(34, 34, 34, 0.4);
border-radius: 50%;
z-index: 2;
height: 70px;
width: 70px;
position: fixed;
top: 0;
bottom: 441rpx;
left: 31rpx;
margin: auto;
.red {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
z-index: 3;
height: 35px;
width: 35px;
}
}
.swiper {
height: 100vh;
.slider {
height: 20rpx;
position: absolute;
bottom: 67rpx;
left: 0;
width: 750rpx;
slider {
margin: 0;
}
}
.swiper-item {
height: 100vh;
position: relative;
.btn {
position: absolute;
width: 120rpx;
height: 120rpx;
background: none;
z-index: 2;
top: 50%;
margin-top: -60rpx;
left: 50%;
margin-left: -60rpx;
&.pause {
animation: benone .2s 2s linear forwards;
}
}
}
}
.video {
width: 100%;
height: 100vh;
position: relative;
}
.left_box {
position: fixed;
bottom: 55px;
left: 0rpx;
width: 100%;
height: 600rpx;
pointer-events: none;
z-index: 8;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1));
.slider {
pointer-events: initial;
height: 20rpx;
position: absolute;
bottom: 67rpx;
left: 0;
width: 750rpx;
slider {
margin: 0;
}
}
::v-deep .u-icon {
position: fixed;
right: 40rpx;
bottom: 280rpx;
pointer-events: initial;
z-index: 10;
}
}
@keyframes benone {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
.uni-padding-wrap {
background-color: #ffffff;
}
</style>
- 已完成!