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

news2024/11/23 4:28:31

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

二、前言:博主一开始一直想实现类似抖音进入页面自动播放当前视频,上下滑动切换之后播放当前视频,但最后在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:**如果出现画面卡顿,声音播放等问题,请一定要关闭视频自动播放功能。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

移远通信推出新一代高算力智能模组SG885G-WF,为工业和消费级IoT应用带来全新性能标杆

2023年7月24日&#xff0c;全球领先的物联网整体解决方案供应商移远通信宣布&#xff0c;正式推出其新一代旗舰级安卓智能模组SG885G-WF。该智能模组具有高达48 TOPS 的AI综合算力、强大性能及丰富的多媒体功能&#xff0c;非常适用于需要高处理能力和多媒体功能的工业和消费者…

T113-S3-RTL8822CU模块驱动移植

目录 前言 一、驱动源码获取 二、源码编译 三、驱动模块加载测试 前言 在嵌入式系统开发中&#xff0c;移植外部硬件设备的驱动是一个常见的任务。本文将分享如何在全志平台上成功移植RTL8822CU无线模块的驱动&#xff0c;以实现无线网络功能。 提示&#xff1a;以下是本篇文…

(四)RabbitMQ高级特性(消费端限流、利用限流实现不公平分发、消息存活时间、优先级队列

Lison <dreamlison163.com>, v1.0.0, 2023.06.23 RabbitMQ高级特性&#xff08;消费端限流、利用限流实现不公平分发、消息存活时间、优先级队列 文章目录 RabbitMQ高级特性&#xff08;消费端限流、利用限流实现不公平分发、消息存活时间、优先级队列消费端限流利用限流…

(一)认识InfluxDB

以下内容来自 尚硅谷&#xff0c;写这一系列的文章&#xff0c;主要是为了方便后续自己的查看&#xff0c;不用带着个PDF找来找去的&#xff0c;太麻烦&#xff01; 第 1 章 认识InfluxDB 1.1 InfluxDB的使用场景 InfluxDB是一种时序数据库&#xff0c;时序数据库通常被用在监…

MySQL笔记——表的修改查询相关的命令操作

系列文章目录 MySQL笔记——MySQL数据库介绍以及在Linux里面安装MySQL数据库&#xff0c;对MySQL数据库的简单操作&#xff0c;MySQL的外接应用程序使用说明 文章目录 系列文章目录 一 表的修改操作 1.1 修改表的名字 1.2 添加一列score 1.3 修改列名称 1.4 修改新增列的…

containerd

Containerd是一个开源的容器运行时&#xff08;Container Runtime&#xff09;&#xff0c;它是Kubernetes和Docker等容器平台的基础组件之一。它旨在提供容器的生命周期管理和基本的运行时功能&#xff0c;使得容器的创建、启动、停止、删除等操作变得简单且高效。 Container…

Apipost使用教程

Apipost是一款集API调试、生成文档、Mock、测试于一体的协同工具。单个工具可以同时满足接口测试、生成/分享文档、Mock、流程测试等功能&#xff0c;还有超实用的多人多角色间实时协作的功能。将前端、后端、测试三种角色串联起来&#xff0c;从而实现工作流程无缝衔接、提高研…

C#中简单Winform程序编译(待验证)

1、文件架构 2、MainWindow.xaml <Window x:Class"WpfApp1.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.microsoft.…

vue中的异步请求Axios(个人学习笔记五)

目录 友情提醒第一章、传统的jQuery方式获取数据1.1&#xff09;后端controller层代码1.2&#xff09;传统的jQuery获取数据1.3&#xff09;使用vue对象和jQuery获取异步数据 第二章、使用Axios获取数据2.1&#xff09;axios简介2.2&#xff09;axios两种使用方式2.3&#xff0…

WEB:easyphp

背景知识 php弱类型比较 MD5碰撞 题目 进行代码审计 <?php highlight_file(__FILE__); $key1 0;//值赋值 $key2 0;$a $_GET[a];//get方法获取值 $b $_GET[b];if(isset($a) && intval($a) > 6000000 && strlen($a) < 3){ //a的值需要大于 60000…

Seaborn中怎样绘制双变量分布图?

两个变量的二元分布可视化也很有用。在 Seaborn中最简单的方法是使用 jointplot()函数&#xff0c;该函数可以创建一个多面板图形&#xff0c;比如散点图、二维直方图、核密度估计等&#xff0c;以显示两个变量之间的双变量关系及每个变量在单坐标轴上的单变量分布。 jointplo…

Linux--Block group

Block Group&#xff1a;ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相 同的结构组成。政府管理各区的例子 超级块&#xff08;Super Block&#xff09;&#xff1a;存放文件系统本身的结构信息。记录的信息主要有&#xff1a;bolck 和 inod…

App测试流程及测试点

1 APP测试基本流程 1.1流程图 1.2测试周期 测试周期可按项目的开发周期来确定测试时间&#xff0c;一般测试时间为两三周&#xff08;即15个工作日&#xff09;&#xff0c;根据项目情况以及版本质量可适当缩短或延长测试时间。正式测试前先向主管确认项目排期。 1.3测试资源…

测试覆盖率 JVM 字节码测试运用 - 远程调试、测试覆盖、影子数据库

目录 前言&#xff1a; 简介 基础使用方式介绍 工具特性 前言&#xff1a; 在软件开发中&#xff0c;测试覆盖率是一个非常重要的指标&#xff0c;它表示代码中所有的测试用例是否都已经被覆盖到。JVM 字节码测试是一种比较新的测试方法&#xff0c;它可以对 JVM 字节码进…

【雕爷学编程】Arduino动手做(86)---4*4位 WS2812 全彩模块4

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

【Postman】Postman接口测试进阶用法详解:断言、全局与环境变量、关联、批量执行用例、读取外部文件实现参数化

文章目录 一、Postman断言1、断言位置2、Postman的常用断言3、操作实例 二、全局变量与环境变量1、二者区分2、设置全局变量3、设置环境变量 三、Postman接口关联1、概念2、操作步骤 四、批量执行测试用例1、操作步骤2、查看结果 五、读取外部文件实现参数化1、使用场景2、操作…

云服务器远程nacos服务注册失败/不健康Client not connected, current status:STARTING

文章目录 Nacos报错docker安装不用 docker安装 Nacos报错 docker安装 使用docker在云服务器安装Nacos之后出现Client not connected, current status:STARTING 使用docker 安装之后需要添加映射端口 docker run -e JAVA_OPTS"-Xms256m -Xmx256m"-e MODEstandalone…

7.24 作业

1.自己封装vector template<typename T> class Myverctor {T* first;T* last;T* end; public:Myverctor():first(NULL),last(NULL),end(NULL){}Myverctor(int num,T data):first(new T[num]){last end first num;for(int i 0;i<num;i) first[i] data;}Myverctor…

【ROS2 Foxy】Rviz2 不支持可视化压缩图像消息

这里我通过订阅话题&#xff0c;压缩图像消息是存在的&#xff1a; ros2 topic echo /hk_camera/rgb/compressed从官方代码库的 issue 中了解到&#xff0c;在 Foxy 版本的 Rviz2 是不支持压缩图像消息的可视化的&#xff0c;现在 Foxy 也已经停止维护了&#xff0c;以后更不太…

redis 1

shell 1&#xff1a;安装1. 源码安装&#xff08;CENTOS&#xff09; 2.999:可能会出现得问题1. 编译出错 1&#xff1a;安装 1. 源码安装&#xff08;CENTOS&#xff09; 官方下载源码包 wget https://download.redis.io/redis-stable.tar.gz # 安装依赖 yum install gcc解压…