uniapp h5 竖向的swiper内嵌视频实现抖音短视频垂直切换,丝滑切换视频效果,无限数据加载不卡顿

一、项目背景:实现仿抖音短视频全屏视频播放、点赞、评论、上下切换视频、视频播放暂停、分页加载、上拉加载下一页、下拉加载上一页等功能。。。

二、前言:博主一开始一直想实现类似抖音进入页面自动播放当前视频,上下滑动切换之后播放当前视频,但最后在ios上出现声音播放,但画面卡顿的问题,估计是ios的浏览器对自动播放做了安全限制,导致自动播放失效,为了功能的可用性,最终放弃自动播放,实现手动点击视频正中心的播放按钮进行播放,再点击视频暂停,这个bug在安卓端暂时没出现,大概率是ios的安全性更高导致的浏览器策略拦截了,需要用户手动交互。

三、项目框架组件:uniapp h5项目、vue2、 swiper组件、video组件

四、效果

仿抖音全屏视频切换播放暂停


在这里插入图片描述

五、布局:可根据自已的项目需求进行修改,博主这里的逻辑是数据由接口返回,如果没有视频,就展示图片,只有视频才进行播放,标题最多展示三行,超过三行显示‘展开’,点击展开谈起标题的底部弹窗,这里弹窗的代码就不展示了,有需要可私信

<view class="widget-video pos-r" :style="{height:`${videoHeight}px`}">
	<swiper class="video-list" :current="current" :style="{height:`${videoHeight}px`}" :vertical="true"
		@change="changeHandler" @transition="transitionHandler" @touchstart="touchStart" @touchend="touchEnd">
		<swiper-item class="video-item" :style="{height:`${videoHeight}px`}" v-for="(item, index) in datas"
			:key="index">
			<video
				v-if="!$util.validatenull(item.videourl) || !$util.validatenull(item.videourl_low) || !$util.validatenull(item.videourl_fhd) || !$util.validatenull(item.videourl_hd)"
				class="thumb-img" :id="`video_${item.id}`" :src="item.videourl" :show-progress="false"
				:show-fullscreen-btn="false" :show-play-btn="false" :loop="true" :show-center-play-btn="false"
				enable-play-gesture :poster="item.thumb" preload="auto" x5-playsinline="" playsinline="true"
				webkit-playsinline="true" x-webkit-airplay="allow" x5-video-player-type="h5"
				x5-video-player-fullscreen="" x5-video-orientation="portraint" @click="playOrpauseFn">
			</video>
			<image v-else class="thumb-img" :src="item.thumb" mode="aspectFit"></image>
			<template v-if="item.videourl || item.videourl_fhd || item.videourl_hd || item.videourl_low">
				<image v-if="showPlayIcon" class="play-icon pos-a pos-tl-c"
					:src="$util.isCandu()?'/static/h5AndWeixin/home/cd_video_play.png':'/static/h5AndWeixin/home/common_icon_item_video_play.png'"
					mode="aspectFill" @tap="playOrpauseFn">
				</image>
			</template>
			<view class="calcwidth pos-a pos-bottom padding-l padding-b">
				<view class="wrapper" @click="openIntroducePop(item.title,item.description)">
					<view :id="'video-title'+item.id" class="c-f video-title"
						:style="{fontSize:$util.isElder()?'39rpx':'30rpx',maxHeight: titleMaxHeight}">
						<text v-if="showExpand" class="expand">展开</text>
						{{item.title}}
					</view>
				</view>
				<from-time-view :item="item" :hideViews="true" :textColor="'#fff'"></from-time-view>
			</view>
			<view class="right-icon-wrap pos-a dflex col-s flex-d pos-right">
				<view v-if="item.allow_comment === 1" class="pos-r tac mt30">
					<image
						:style="{width:$util.isElder()?'104rpx':'80rpx',height:$util.isElder()?'104rpx':'80rpx'}"
						src="/sub-live/static/comment.png"
						@click="openCommentPop(item.catid,item.contentid,item.id)" mode="scaleToFill">
					</image>
					<view v-if="commentCount> 0" class="zan-num tac pos-a pos-b-full"
						:style="{fontSize:$util.isElder()?'32rpx':'20rpx',backgroundColor:$config.INFO.SUB_THEME_COLOR}">
						{{$util.filterViews(commentCount)}}
					</view>
				</view>
				<view v-if="item.islike === 1" class="pos-r tac mt30">
					<image
						:style="{width:$util.isElder()?'104rpx':'80rpx',height:$util.isElder()?'104rpx':'80rpx'}"
						:src="likeObj.liked ? '/sub-live/static/zan-active.png' : '/sub-live/static/zan-inactive.png'"
						mode="scaleToFill" @click="goZanFn(item.catid,item.id)">
					</image>
					<view v-if="likeObj.like_count > 0" class="zan-num tac pos-a pos-b-full"
						:style="{fontSize:$util.isElder()?'32rpx':'20rpx',backgroundColor:$config.INFO.SUB_THEME_COLOR}">
						{{$util.filterViews(likeObj.like_count)}}
					</view>
				</view>
			</view>
		</swiper-item>
	</swiper>
	<view class="nav-bar dflex padding-left-and-right pos-a pos-top">
		<image :style="{width:$util.isElder()?'39rpx':'30rpx',height:$util.isElder()?'39rpx':'30rpx'}"
			src="/static/h5AndWeixin/public/white-back.png" @click="goBack"></image>
	</view>
</view>

六、js:主要展示视频代码

data() {
	return {
		videoHeight: uni.getWindowInfo().windowHeight,
		current: 0,
		datas: [],
		page: 0, // 当前页0,上一页-1,下一页1
		showPlayIcon: false,
		pageStartY: 0,
		pageEndY: 0,
		titleMaxHeight: '',
		showExpand: false,
		videoCtx: null
	};
},
onLoad() {
	// 获取当前页数据
	this.getvideolists();
},
methods: {
	getvideolists() {
		const _this = this;
		// 请求数据,改成自已接口的路径和参数
		_this.$api.getVerticalVideoList({
			catid: _this.catid,
			id: _this.id, // 请求上一页传第一条数据的id,请求下一页传最后一条数据的id
			page: _this.page
		}).then(res => {
			if (res.data) {
				// 判断是否有数据,有数据才进行操作
				if (!_this.$util.validatenull(res.data.lists)) {
					// 下拉加载上一页,将数据插入当前数据的头部,并且播放数据的最后一条
					if (_this.current === 0 && _this.page === -1) {
						_this.datas.unshift(...res.data.lists);
						_this.current = res.data.lists.length - 1;
					} else {
						// 上拉加载下一页,将数据添加到当前数据的尾部
						_this.datas.push(...res.data.lists);
					}
					const firstItem = _this.datas[0];
					// 只创建当前视频的播放器,以免卡顿
					_this.playOrpauseFn();
				} 
			}
		}).catch((err) => {
			console.error(err);
		})
	},
	// 上下切换视频
	changeHandler(e) {
		const _this = this;
		if (e.detail.source == 'touch') {
			const {
				current
			} = e.detail;
			// 将播放按钮隐藏
			_this.showPlayIcon = false;
			// 设置当前视频
			_this.current = current;
			// 只创建当前视频播放器,播放当前视频,暂停其他视频
			_this.playOrpauseFn();
		}
	},
	transitionHandler(e) {
		if (e.detail.dy === 0) {
			// 最后一条数据上拉加载下一页
			if (this.current === this.datas.length - 1) {
				if (this.pageStartY > this.pageEndY) {
					this.page = 1;
					this.id = this.datas.at(-1).id;
					this.getvideolists();
				}
			}
			// 第一条数据下拉加载上一页
			if (this.current === 0) {
				if (this.pageStartY < this.pageEndY) {
					this.page = -1;
					this.id = this.datas.at(0).id;
					this.getvideolists();
				}
			}
		}
	},
	// 获取当前触发的纵坐标以此来判断是上拉还是下拉
	// 记录开始滑动的手指的纵坐标
	touchStart(res) {
		if (this.current === this.datas.length - 1 || this.current === 0) {
			this.pageStartY = res.changedTouches[0].pageY;
		}
	},
	// 记录滑动结束的手指的纵坐标
	touchEnd(res) {
		if (this.current === this.datas.length - 1 || this.current === 0) {
			this.pageEndY = res.changedTouches[0].pageY;
		}
	},
	// 根据视频id创建播放器
	playOrpauseFn() {
		let video_id = this.datas[this.current].id;
		this.videoCtx = uni.createVideoContext(`video_${video_id}`, this);
		// 点击播放按钮视频播放,按钮隐藏,再点击视频暂停,按钮显示
		if (this.showPlayIcon) {
			this.videoCtx.seek(0);
			this.videoCtx.play();
			this.showPlayIcon = false;
		} else {
			this.videoCtx.pause();
			this.showPlayIcon = true;
		}
	}
}

七、sass:

	.widget-video {
		width: 100%;
		background-color: #000;
		overflow: hidden;

		.nav-bar {
			width: 100%;
			height: 88rpx;
		}
	}


	.video-list {
		width: 100%;
		height: 100%;

		.video-item {
			width: 100%;
			position: relative;

			.play-icon {
				width: 64rpx;
				height: 64rpx;
			}

			.right-icon-wrap {
				// width: 112rpx;
				bottom: 208rpx;
				right: 18rpx;

				.mt30 {
					margin-top: 60rpx;
				}

				.zan-num {
					// width: 68rpx;
					margin: auto;
					border-radius: 4rpx;
					font-weight: 600;
					color: #FFFFFF;
					transform: scale(0.8);
				}
			}

			.calcwidth {
				width: calc(100% - 130rpx);
			}

			.wrapper {
				display: flex;
				width: 100%;
				overflow: hidden;

				.video-title {
					overflow: hidden;
					text-overflow: ellipsis;
					text-align: justify;
					position: relative;
				}

				.video-title::before {
					content: '';
					height: calc(100% - 42rpx);
					float: right;
				}

				.expand {
					position: relative;
					float: right;
					clear: both;
					margin-left: 40rpx;
					color: #A9A9B8;
					cursor: pointer;
					border: 0;
				}

				.expand::before {
					content: '...';
					position: absolute;
					left: -10rpx;
					color: #fff;
					transform: translateX(-100%);
				}
			}

		}
	}

**end:**如果出现画面卡顿,声音播放等问题,请一定要关闭视频自动播放功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/50825.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CAN学习笔记3:STM32 CAN控制器介绍

STM32 CAN控制器 1 概述 STM32 CAN控制器&#xff08;bxCAN&#xff09;&#xff0c;支持CAN 2.0A 和 CAN 2.0B Active版本协议。CAN 2.0A 只能处理标准数据帧且扩展帧的内容会识别错误&#xff0c;而CAN 2.0B Active 可以处理标准数据帧和扩展数据帧。 2 bxCAN 特性 波特率…

24考研数据结构-数组和特殊矩阵

目录 数据结构&#xff1a;数组与特殊矩阵数组数组的特点数组的用途 特殊矩阵对角矩阵上三角矩阵和下三角矩阵稀疏矩阵特殊矩阵的用途 结论 3.4 数组和特殊矩阵3.4.1数组的存储结构3.4.2普通矩阵的存储3.4.3特殊矩阵的存储1. 对称矩阵(方阵)2. 三角矩阵(方阵)3. 三对角矩阵(方阵…

【Go语言】Golang保姆级入门教程 Go初学者介绍chapter1

Golang 开山篇 Golang的学习方向 区块链研发工程师&#xff1a; 去中心化 虚拟货币 金融 Go服务器端、游戏软件工程师 &#xff1a; C C 处理日志 数据打包 文件系统 数据处理 很厉害 处理大并发 Golang分布式、云计算软件工程师&#xff1a;盛大云 cdn 京东 消息推送 分布式文…

汉明距离,两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。

题记&#xff1a; 两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。 给你两个整数 x 和 y&#xff0c;计算并返回它们之间的汉明距离。 示例 1&#xff1a; 输入&#xff1a;x 1, y 4 输出&#xff1a;2 解释&#xff1a; 1 (0 0 0 1) 4 (0 1 0 0…

spring5源码篇(13)——spring mvc无xml整合tomcat与父子容器的启动

spring-framework 版本&#xff1a;v5.3.19 文章目录 整合步骤实现原理ServletContainerInitializer与WebApplicationInitializer父容器的启动子容器的启动 相关面试题 整合步骤 试想这么一个场景。只用 spring mvc&#xff08;确切来说是spring-framework&#xff09;&#x…

PostgreSQL 简洁、使用、正排索引与倒排索引、空间搜索、用户与角色

PostgreSQL使用 PostgreSQL 是一个免费的对象-关系数据库服务器(ORDBMS)&#xff0c;在灵活的BSD许可证下发行。PostgreSQL 9.0 &#xff1a;支持64位windows系统&#xff0c;异步流数据复制、Hot Standby&#xff1b;生产环境主流的版本是PostgreSQL 12 BSD协议 与 GPL协议 …

TypeScript -- 类

文章目录 TypeScript -- 类TS -- 类的概念创建一个简单的ts类继承 public / private / protected-- 公共/私有/受保护的public -- 公共private -- 私有的protected -- 受保护的 其他特性readonly -- 只读属性静态属性 -- static修饰ts的getter /setter抽象类abstract TypeScrip…

【深入理解NAND Flash】 闪存(NAND Flash) 学习指南

依公开知识及经验整理&#xff0c;付费内容&#xff0c;禁止转载。 所在专栏 《深入理解Flash:闪存特性与实践》 1. 我想和你说 漠然回首&#xff0c;从事存储芯片行业已多年&#xff0c;这些年最宝贵的青春都献给了闪存&#xff0c;虽不说如数家珍&#xff0c;但也算专业。 …

Nginx下载、安装与使用

Nginx下载 简介&#xff1a; Nginx是一个高性能的HTTP和反向代理web服务器&#xff0c;同时也提供了IMAP/POP3/SMTP服务&#xff08;邮件服务&#xff09;。 官网下载地址&#xff1a; https://nginx.org/en/download.html 国内镜像地址&#xff1a; https://mirrors.huawe…

华为云NFS使用API删除大文件目录

最近在使用华为云SFS时&#xff0c;如果一个目录存储文件数超过100W&#xff0c;执行 “rm -rf path”时&#xff0c;存在删不动的情况&#xff0c;可以使用华为云API接口&#xff0c;执行异步删除。 华为官网&#xff1a; 删除文件系统目录_弹性文件服务 SFS_API参考_SFS Tu…

Redis入门

一、Redis的安装 Redis的官方文档介绍了多种安装方式(包括Linux、Windows、MacOs平台上的安装和从源码包安装)&#xff1a;Redis安装。这里只介绍源码安装方式。 下载源码包 $ wget https://download.redis.io/redis-stable.tar.gz编译Redis $ tar -xzvf redis-stable.tar.gz …

Linux下进程特性总结:工作目录,环境变量,标准输出转命令行参数,O_CLOEXEC标志作用,读写锁控制进程互斥

进程是运行中的程序&#xff0c;是资源分配的最小单位&#xff0c;其有一些特性对于实际开发很有帮助&#xff0c;本篇博客将进程的相关特性进行梳理总结&#xff0c;包含工作目录&#xff0c;环境变量&#xff0c;标准输出转命令行参数&#xff0c;读写锁控制进程互斥。 目录…

MyBatis学习笔记之缓存

文章目录 一级缓存一级缓存失效 二级缓存二级缓存失效二级缓存相关配置 MyBatis集成EhCache 缓存&#xff1a;cache 缓存的作用&#xff1a;通过减少IO的方式&#xff0c;来提高程序的执行效率 mybatis的缓存&#xff1a;将select语句的查询结果放到缓存&#xff08;内存&…

Pytorch深度学习-----神经网络的卷积操作

系列文章目录 PyTorch深度学习——Anaconda和PyTorch安装 Pytorch深度学习-----数据模块Dataset类 Pytorch深度学习------TensorBoard的使用 Pytorch深度学习------Torchvision中Transforms的使用&#xff08;ToTensor&#xff0c;Normalize&#xff0c;Resize &#xff0c;Co…

IDEA将本地项目上传到码云

一、创建本地仓库并关联 用IDEA打开项目&#xff0c;在菜单栏点击vcs->create git repository创建本地仓库&#xff0c; 选择当前项目所在的文件夹当作仓库目录。 二、将项目提交本地仓库 项目名右键就会出现“GIT”这个选项->Add->Commit Directory, 先将项目add…

【嵌入式学习笔记】嵌入式基础11——STM32常用轮子(SYSTEM)

1.deley文件夹介绍 1.1.delay文件夹介绍 函数名函数功能OSdelay_osschedlockus级延时时,关闭任务调度(防止打断us级延迟)OSdelay_osschedunlockus级延时时,恢复任务调度OSdelay_ostimedlyus级延时时,恢复任务调度OSSysTick_Handlersystick中断服务函数OSdelay_init初始化延迟…

【学习笔记】关于图像YUV格式分类和排布方式的全学习

这里是尼德兰的喵学习笔记相关文章&#xff0c;欢迎您的访问&#xff01; 如果文章对您有所帮助&#xff0c;期待您的点赞收藏 让我们一起为芯片前端全栈工程师而努力 目录 前言 YUV格式导图 YUV444 packed planar I444 YV24 semi-planar NV24 NV42 YUV422 packed …

c++静态代码扫描工具clang-tidy详细介绍

clang-tidy 文章目录 clang-tidy1. 什么是clang-tidy2. clang-tidy可以解决什么问题3. 工作原理4. 如何使用clang-tidy4. 总结5. 举例说明&#xff1a; 1. 什么是clang-tidy Clang-Tidy是一个由LLVM项目提供的开源工具&#xff0c;是一个静态分析工具&#xff0c;用于进行静态…

【雕爷学编程】MicroPython动手做(15)——掌控板之AB按键3

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

win10日程怎么同步到安卓手机?电脑日程同步到手机方法

在如今快节奏的生活中&#xff0c;高效地管理时间变得至关重要。而对于那些经常在电脑上安排日程的人来说&#xff0c;将这些重要的事务同步到手机上成为了一个迫切的需求。因为目前国内使用win10系统电脑、安卓手机的用户较多&#xff0c;所以越来越多的职场人士想要知道&…