uni-app上下滑动播放视频(类抖音)

uni-app 上下滑动播放视频

1、视频列表页

<template>
	<view>
		<view class="f_bottom_box" v-if="videoList.length">
			<view class="bottom_box" v-for="(vo,index) in videoList" :key="index" @click="goXiangqing(vo.id)">
				<image :src="vo.cover" mode="widthFix" class="bot_img" v-if="vo.cover" :lazy-load="true"></image>
				<video @loadeddata="createPreImg($event)" :src="vo.video" mode="widthFix" class="bot_img"
					v-else-if="vo.video"></video>
				<image :src="vo.imageList[0].url" mode="widthFix" class="bot_img" v-else :lazy-load="true"></image>
				<view class="bottom_img_text">
					<view class="text">{{vo.title}}</view>
					<view class="bottom_img" @click.stop>
						<view class="f_img">
							<image :src="vo.touxiang" mode="aspectFill" class="tx"></image>
							<view class="">{{vo.name}}</view>
						</view>
						<u-icon name="heart-fill" color="rgba(216, 32, 70, 1)" size="18" class="love"
							v-if="vo.isdianzan" @click="isLOve(vo.id,0,index)"></u-icon>
						<u-icon name="heart" color="rgba(196, 196, 196, 1)" size="18" class="love" v-else
							@click="isLOve(vo.id,1,index)"></u-icon>
					</view>
				</view>
			</view>
		</view>

	</view>
</template>

<script>
	import app from '../../App.vue';
	export default {
		data() {
			return {
				videoList: [],
			}
		},
		onLoad() {
			this.getVideoList()
		},
		methods: {
			// 获取所有视频列表
			getVideoList() {
				let data = {
					uid: app.globalData.uid
				}
				this.yrApi.request('Zuopin/getVideoList', 'POST', data).then(res => {
					if (res.data.code == 0) {
						this.videoList = res.data.data
					}
				})
			},
			// 跳转到刷视频界面
			goXiangqing(id){
				uni.navigateTo({
					url:`/pages/demo/shipinxiashua?id=${id}&videoList=${JSON.stringify(this.videoList)}`
				})
			}
		}
	}
</script>

<style lang="scss">
	.f_bottom_box {
		// display: flex;
		// align-items: center;
		// // flex-wrap: wrap;
		// overflow: hidden;
		padding: 0 30rpx;
		justify-content: space-between;
		// flex-flow: column wrap;
		// height: 100vh;
		// display: grid;
		// column-count: 2;//想要排成的列数
		// column-gap: 10;
		column-count: 2;
		column-gap: 10px;
		margin: 0 auto;

		.bottom_box {
			// display: inline-block;
			// width: 48%;
			// margin-left: 4%;
			// vertical-align: top;
			border-radius: 10rpx;
			background-color: #fff;
			box-sizing: border-box;
			-webkit-column-break-inside: avoid;
			break-inside: avoid;
			// text-align: center;
			margin-bottom: 20rpx;

			// &:nth-child(2n-1){
			// 	margin-left: 0;
			// }
			.bot_img {
				width: 100%;
				// height: 439rpx;
				border-radius: 10rpx 10rpx 0 0;
			}

			.bottom_img_text {
				box-sizing: border-box;
				padding: 14rpx 13rpx 20rpx 13rpx;

				.text {
					font-size: 30rpx;
					font-weight: 500;
					color: #434342;
					margin-bottom: 10rpx;
				}

				.bottom_img {
					display: flex;
					justify-content: space-between;
					align-items: center;

					.f_img {
						display: flex;
						align-items: center;

						.tx {
							width: 39rpx;
							height: 39rpx;
							border-radius: 50%;
							margin-right: 10rpx;
						}

						view {
							font-size: 22rpx;
							font-weight: 400;
							color: #999999;
							line-height: 30rpx;
						}
					}


					.love {
						width: 30rpx;
						height: 30rpx;
					}
				}
			}
		}
	}
</style>

2、视频详情页

<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="current" :circular="false" :vertical="true">
						<block v-if="PayVideo">
							<swiper-item v-for="(item,index) in PayVideo" :key="index">
								<view class="swiper-item uni-bg-black">
									<video
										:custom-cache="false" 
										:show-fullscreen-btn="false"
										:controls="true"
										:show-center-play-btn="false"
										enable-play-gesture
										:initial-time="0"
										class="video" 
										:id="'id'+index" 
										:src="item.video"
										loop
										:enable-progress-gesture="false"
										v-if="index == current"
									>
									</video>
								</view>
							</swiper-item>
						</block>
                    </swiper>
                </view>
            </view>
        </view>
        <view>
            <view class="left">
                <cover-view class="left_box">
                    <cover-view class="ke_context height">{{description}}</cover-view>
					<!-- <cover-view class="auto">
						<cover-view>视频总时长:{{duration || 0}}</cover-view>
					</cover-view> -->
                </cover-view>
            </view>
        </view>
    </view>
</template>
 
<script>
    export default {
        data() {
            return {
                PayVideo: [],
				videoData: {},
				videoList: [],
				description: '',
				current: 0,
				index_: 0,
				videoContext: '',
				duration: '',//总视频时长
            }
        },
        computed: {
        },
		onLoad(options) {
			this.getVideoInfo(parseInt(options.id))
			if(options.videoList) {
				this.videoList = JSON.parse(decodeURIComponent(options.videoList))
			}
		},
        methods: {
			getVideoInfo(id){
				let data = {
					id
				}
				this.yrApi.request('Zuopin/getVideoById', 'POST', data).then(res=>{
					if(res.data.code==0){
						this.videoData = res.data.data;
						this.id = res.data.data.id
						uni.setNavigationBarTitle({
							title:this.videoData.title
						})
						this.description = this.videoData.desc
						this.PayVideo = this.videoList //上下滑动总数据
						this.current = this.PayVideo.findIndex((item) => {
							return item.id === res.data.data.id
						})
						this.duration = res.data.data.videoTime
						// 自动播放当前视频
						this.$nextTick(function() {
							let videoContext = uni.createVideoContext('id' + this.current)
							console.log("index----",videoContext)
							setTimeout(() => {
							      videoContext.play()
							}, 500)
						})
					}else{
						uni.showToast({
							title:res.msg,
							icon:'none'
						})
						// setTimeout(()=>{
						// 	uni.redirectTo({
						// 		url:'/pages/index/index?index=2'
						// 	})
						// },1500)
					}
				})
			},
			// current改变时会触发change 事件
            changefun(e) {
                let current = e.detail.current
				uni.setNavigationBarTitle({
					title: this.PayVideo[current].title
				})
				if(this.PayVideo[current].desc) {
					this.description = this.PayVideo[current].desc
				}
				this.duration = this.PayVideo[current].videoTime
				let videoContext = uni.createVideoContext('id' + this.current)
				videoContext.pause() //停止播放
            },
			// 动画结束时触发
			animationfinishfun(e) {
				let current = e.detail.current
				let videoContext = uni.createVideoContext('id' + this.current)
				videoContext.pause() //停止播放非当前视频
				videoContext = uni.createVideoContext('id' + current)
				videoContext.play() //开始播放当前视频
				this.current = current //保存当前下标 播放下一个视频时停止上一个视频 防止声音重复
			}
        },
    }
</script>
 
<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;
 
        .swiper-item {
            height: 100vh;
            position: relative;
        }
 
        .uni-bg-black {
            background: black;
        }
    }
 
    .video {
        width: 100%;
        height: 98vh;
        position: relative;
    }
 
    .left_box {
        position: fixed;
        bottom: 90rpx;
        left: 24rpx;
        .ke_context {
			overflow-y: scroll;
			max-height: 300rpx;
			white-space:pre-wrap;
            width: 700rpx;
            font-size: 30rpx;
            font-family: PingFang SC;
            font-weight: 500;
            color: rgba(255, 255, 255, 1);
			white-space:pre-wrap;
            text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.4);
        }
		
		.height {
			margin-bottom: 40rpx;
		}
 
        .ren {
            margin: 20rpx 0;
            font-size: 36rpx;
            font-family: PingFang SC;
            font-weight: bold;
            color: rgba(255, 255, 255, 1);
            text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.4);
        }
 
        .auto {
            display: flex;
            align-items: center;
            width: 310rpx;
            height: 60rpx;
            margin-top: 24rpx;
            opacity: 1;
            border-radius: 8rpx;
            padding-left: 10rpx;
 
            cover-view {
                font-size: 26rpx;
                font-family: PingFang SC;
                font-weight: 500;
                color: rgba(255, 255, 255, 1);
                line-height: 90px;
                text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.4);
            }
 
            cover-image {
                margin-right: 10rpx;
                height: 28rpx;
                width: 26rpx;
            }
        }
 
        .ke {
            display: flex;
            align-items: center;
            width: 310rpx;
            height: 60rpx;
            background: rgba(0, 0, 0, 0.3);
            opacity: 1;
            border-radius: 8rpx;
            padding-left: 10rpx;
 
            cover-view {
                font-size: 26rpx;
                font-family: PingFang SC;
                font-weight: 500;
                color: rgba(255, 255, 255, 1);
                line-height: 90px;
                text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.4);
            }
 
            cover-image {
                margin-right: 10rpx;
                height: 40rpx;
                width: 40rpx;
            }
        }
    }
 
    .H_T {
        z-index: 2;
        width: 100%;
        display: flex;
        box-sizing: border-box;
        position: absolute;
        padding: 0 30rpx;
        background: transparent;
        justify-content: space-between;
 
        .back {
            height: 48rpx;
            width: 48rpx;
        }
 
        .search {
            height: 48rpx;
            width: 48rpx;
        }
    }
 
    .right_box {
        width: 100rpx;
        position: absolute;
        z-index: 2;
        bottom: 60rpx;
        right: 12rpx;
        display: flex;
        flex-direction: column;
 
        .top1 {
            position: relative;
            height: 124rpx;
 
            .avatar_img {
                width: 98rpx;
                height: 98rpx;
                border-radius: 50%;
            }
 
            .add_img {
                position: absolute;
                z-index: 99;
                width: 48rpx;
                height: 48rpx;
                bottom: 10rpx;
                left: 0;
                right: 0;
                margin: 0 auto;
            }
        }
 
        .top2 {
            display: flex;
            flex-direction: column;
            align-items: center;
            margin-top: 37rpx;
 
            .t_img {
                height: 72rpx;
                width: 72rpx;
                margin-bottom: 10rpx;
            }
            
            .font_t {
                font-size: 26rpx;
                font-family: SF Pro Text;
                font-weight: 500;
                color: rgba(255, 255, 255, 1);
 
                text-shadow: 0px 1rpx 1rpx rgba(0, 0, 0, 0.4);
                text-align: center;
            }
        }
 
    }
</style>

### UniApp集成视频播放器 #### 准备工作 为了在UniApp项目中成功集成风格的视频播放器,需先搭建好开发环境。这包括安装并配置HBuilderX作为IDE工具,以及Node.js用于支持必要的构建过程[^1]。 #### 创建新项目 启动HBuilderX后创建一个新的`uni-app`项目,在此过程中可以选择模板来加速初期设置阶段的工作效率。对于本案例而言,默认空白页面即足够满足需求[^2]。 #### 安装依赖库 通过npm或yarn命令行工具向项目添加所需的第三方组件包,特别是那些能够增强媒体处理能力的库文件。例如,可以考虑引入`vue-video-player`这样的Vue插件以便更好地管理多媒体资源展示逻辑。 ```bash npm install vue-video-player --save ``` #### 配置全局样式与脚本 编辑项目的入口HTML文档(通常是index.html),确保已正确加载了所选视频播放控件的相关CSS和JS链接地址。如果采用的是本地打包方式,则应确认这些静态资产已被妥善放置于合适目录下,并且路径声明无误。 #### 编写核心业务代码 针对具体应用场景定制化设计UI布局结构的同时也要兼顾交互行为定义。下面给出了一段简化版示例代码片段,展示了如何利用`<video>`标签配合自定义属性完成基本功能实现: ```html <!-- pages/index/index.vue --> <template> <view class="container"> <!-- 单个视频--> <swiper :current="currentIndex" @change="onChangeSwiperItem"> <block v-for="(item, index) in videoList" :key="index"> <swiper-item> <video id="myVideo" :src="item.url" controls></video> </swiper-item> </block> </swiper> <!-- 上下滑动切换按钮组 --> <button type="primary" size="mini" @click="prev">上一个</button> <button type="default" size="mini" @click="next">下一个</button> </view> </template> <script> export default { data() { return { currentIndex: 0, videoList: [ { url: 'https://example.com/video1.mp4' }, { url: 'https://example.com/video2.mp4' } ] }; }, methods: { onChangeSwiperItem(e){ this.currentIndex = e.detail.current; }, prev(){ const length = this.videoList.length; let newIndex = (this.currentIndex - 1 + length)%length; uni.createSelectorQuery().select(`#myVideo`).fields({node:true},res=>{ res.node.seek(0); }).exec(); this.$refs.swiperRef.setCurrent(newIndex); }, next(){ const length = this.videoList.length; let newIndex = (this.currentIndex + 1)%length; uni.createSelectorQuery().select(`#myVideo`).fields({node:true},res=>{ res.node.seek(0); }).exec(); this.$refs.swiperRef.setCurrent(newIndex); } } }; </script> ``` 上述代码实现了简单的轮播效果,允许用户点击底部导航栏上的两个按键来进行前后翻页操作;每当触发一次变换事件时都会重置当前正在播放中的影片位置至起始处。 #### 测试验证 最后一步是在模拟器或是真实设备环境中运行应用程序以检验实际表现情况是否符合预期目标。考虑到不同操作系统间可能存在细微差异,建议尽可能多地覆盖多种终端型进行全面测试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值