uniapp 开发之仿抖音,上下滑动切换视频、点击小爱心效果
效果图:
功能描述:
上下滑动视频,双击暂停,然后第一个视频再往上滑显示”已经滑到顶了“
开始代码:
首先视频接口使用的公开的视频测试接口
开放API-2.0 官网展示 Swagger UI 接口文档
一开始编写如下:
<template><view><!--swiper实现整屏划动播放视频--><swiper circular vertical duration="200" @transition="transition" @change="changed":style="{height: screenHeight-navBarHeight +'px'}"><block v-for="(item,index) in displaySwiperList" :key="index"><swiper-item><!-- v-if="index==changeIndex" 只渲染当前页的视频,能够有效解决数组不断追加后引起黑屏的问题 --><video v-if="index==changeIndex" :src="item.src" autoplay="true" controls="true"custom-cache="false" loop="false" enable-play-gesture="true" enable-progress-gesture="true"show-center-play-btn="true"></video><!-- 文本标题 --><view class="video-text"><view class="tips"> {{item.title}} </view></view></swiper-item></block></swiper></view>
</template><script>export default {data() {return {screenHeight: 0,statusBarHeight: 0,navBarHeight: 0,originList: [], // 源数据displaySwiperList: [], // swiper需要的数据displayIndex: 0, // 用于显示swiper的真正的下标数值只有:0,1,2。originIndex: 0, // 记录源数据的下标changeIndex: 0, //控制video是否渲染page: 0, // 视频分页num: 0,flag: true}},onLoad() {/* 获取系统信息 */wx.getSystemInfo({success: (res) => {// 获取屏幕高度this.screenHeight = res.screenHeight// 获取状态栏高度this.statusBarHeight = res.statusBarHeight// 通过操作系统 确定自定义导航栏高度 if (res.system.substring(0, 3) == "iOS") {this.navBarHeight = 42} else {this.navBarHeight = 40}}})// 调用函数this.getPageID()},methods: {/* 生成随机的 pageID */getPageID() {let pageID = parseInt(Math.random() * (0 - 100 + 1) + 100) //生成 [min,max] 的随机数this.getVideoList(pageID)},/* 获取视频数据 */getVideoList(pageID) {uni.request({url: 'https://api.apiopen.top/api/getMiniVideo?page=' + pageID +'&size=10&pageSize=10', // 请求数据接口data: {},success: (res) => {if (res.data.code == 200) {res.data.result.list.forEach(item => {//取源数据的部分属性组合成新的数组let obj = {}obj.src = item.playurlobj.title = item.titlethis.originList.push(obj)})}//解决首次加载页面的时候没有画面的问题if (this.flag) {this.flag = falsethis.initSwiperData(0)}}})},changed(event) {let {current} = event.detail;let originListLength = this.originList.length;this.changeIndex = current;// console.log(this.displayIndex,current)// 如果两者的差为2或者-1则是向后滑动if (this.displayIndex - current == 2 || this.displayIndex - current == -1) {this.originIndex = this.originIndex + 1 == originListLength ? 0 : this.originIndex + 1;this.displayIndex = this.displayIndex + 1 == 3 ? 0 : this.displayIndex + 1;this.initSwiperData(this.originIndex);//如果滑到最后一条,请求新数据this.num++console.log('num',this.num,this.originList.length)if (this.num + 5 >= this.originList.length) {this.getPageID()}}// 如果两者的差为-2或者1则是向前滑动else if (this.displayIndex - current == -2 || this.displayIndex - current == 1) {this.originIndex = this.originIndex - 1 == -1 ? originListLength - 1 : this.originIndex - 1;this.displayIndex = this.displayIndex - 1 == -1 ? 2 : this.displayIndex - 1;this.initSwiperData(this.originIndex);if (this.num > 0) {this.num--}}},initSwiperData(originIndex = this.originIndex) {// console.log(this.displayIndex,originIndex)// 0 0// 1 1// 2 2// 0 3// 1 4//源数据长度let originListLength = this.originList.length;let displayList = [];displayList[this.displayIndex] = this.originList[originIndex];displayList[this.displayIndex - 1 == -1 ? 2 : this.displayIndex - 1] = this.originList[originIndex - 1 == -1 ? originListLength - 1 : originIndex - 1];displayList[this.displayIndex + 1 == 3 ? 0 : this.displayIndex + 1] = this.originList[originIndex + 1 ==originListLength ? 0 : originIndex + 1];// console.log(originIndex, (originIndex - 1 == -1 ? originListLength - 1 : originIndex - 1), (originIndex +// 1 == originListLength ? 0 : originIndex + 1))// 0 9 1// 1 0 2// 2 1 3// 3 2 4// 4 3 5//刷新数据this.displaySwiperList = displayList;// console.log(this.displaySwiperList,this.originList)},}}
</script><style>swiper {width: 100%;background: #000}swiper-item {height: 100%;width: 100%}video {height: 96%;width: 100%}.video-text {position: absolute;margin-left: 32rpx;width: 580rpx;bottom: 200rpx;z-index: 9999;}.tips {width: 560rpx;font-size: 26rpx;color: #ffffff;}
</style>
注解:
autoplay="true"
:设置视频在加载完成后自动播放。controls="true"
:显示视频的控制面板,包括播放/暂停按钮、音量控制、进度条和全屏按钮等。custom-cache="false"
:禁用视频的自定义缓存,在每次播放时都重新加载视频。loop="false"
:设置视频不循环播放,当播放完成后停止。enable-play-gesture="true"
:启用手势控制,允许通过手势来播放/暂停视频。enable-progress-gesture="true"
:启用手势控制,允许通过手势来调整视频播放的进度。show-center-play-btn="true"
:显示一个居中的播放按钮,当视频处于暂停状态时,点击按钮可以播放视频。
进一步希望能够实现上滑到第一个视频之后,关闭循环 无法再上滑
<swiper :circular="!canCircular" >
</swiper>computed: {canCircular() {console.log(Boolean((this.originIndex + 1 == this.originList.length ? 0 : this.originIndex + 1) == 1))return (this.originIndex + 1 == this.originList.length ? 0 : this.originIndex + 1) == 1; }
}
第一个视频再上滑 弹出提示框
<swiper @transition="transition">
</swiper>transition(e) {// console.log(e)let originListLength = this.originList.length;if ((this.originIndex + 1 == originListLength ? 0 : this.originIndex + 1) == 1 && e.detail.dy < -100) {uni.showToast({title: '已经到顶了',icon: 'none'})}
}
注解:
swiper-item 的位置发生改变时会触发 transition 事件,通过判断是否为第一个视频 && 进行了上滑行为 来控制弹出”已经到顶的提示“
完整代码:
<template><view><!--swiper实现整屏划动播放视频--><swiper :circular="!canCircular" vertical duration="200" @transition="transition" @change="changed":style="{height: screenHeight-navBarHeight +'px'}"><block v-for="(item,index) in displaySwiperList" :key="index"><swiper-item><!-- v-if="index==changeIndex" 只渲染当前页的视频,能够有效解决数组不断追加后引起黑屏的问题 --><video v-if="index==changeIndex" :src="item.src" autoplay="true" controls="true"custom-cache="false" loop="false" enable-play-gesture="true" enable-progress-gesture="true"show-center-play-btn="true"></video><!-- 文本标题 --><view class="video-text"><view class="tips"> {{item.title}} </view></view></swiper-item></block></swiper></view>
</template><script>export default {data() {return {screenHeight: 0,statusBarHeight: 0,navBarHeight: 0,originList: [], // 源数据displaySwiperList: [], // swiper需要的数据displayIndex: 0, // 用于显示swiper的真正的下标数值只有:0,1,2。originIndex: 0, // 记录源数据的下标changeIndex: 0, //控制video是否渲染page: 0, // 视频分页num: 0,flag: true}},computed: {canCircular() {console.log(Boolean((this.originIndex + 1 == this.originList.length ? 0 : this.originIndex + 1) == 1))return (this.originIndex + 1 == this.originList.length ? 0 : this.originIndex + 1) == 1; }},onLoad() {/* 获取系统信息 */wx.getSystemInfo({success: (res) => {// 获取屏幕高度this.screenHeight = res.screenHeight// 获取状态栏高度this.statusBarHeight = res.statusBarHeight// 通过操作系统 确定自定义导航栏高度 if (res.system.substring(0, 3) == "iOS") {this.navBarHeight = 42} else {this.navBarHeight = 40}}})// 调用函数this.getPageID()},methods: {transition(e) {// console.log(e)let originListLength = this.originList.length;if ((this.originIndex + 1 == originListLength ? 0 : this.originIndex + 1) == 1 && e.detail.dy < -100) {uni.showToast({title: '已经到顶了',icon: 'none'})}},/* 生成随机的 pageID */getPageID() {let pageID = parseInt(Math.random() * (0 - 100 + 1) + 100) //生成 [min,max] 的随机数this.getVideoList(pageID)},/* 获取视频数据 */getVideoList(pageID) {uni.request({url: 'https://api.apiopen.top/api/getMiniVideo?page=' + pageID +'&size=10&pageSize=10', // 请求数据接口data: {},success: (res) => {if (res.data.code == 200) {res.data.result.list.forEach(item => {//取源数据的部分属性组合成新的数组let obj = {}obj.src = item.playurlobj.title = item.titlethis.originList.push(obj)})}//解决首次加载页面的时候没有画面的问题if (this.flag) {this.flag = falsethis.initSwiperData(0)}}})},changed(event) {let {current} = event.detail;let originListLength = this.originList.length;this.changeIndex = current;// console.log(this.displayIndex,current)// 如果两者的差为2或者-1则是向后滑动if (this.displayIndex - current == 2 || this.displayIndex - current == -1) {this.originIndex = this.originIndex + 1 == originListLength ? 0 : this.originIndex + 1;this.displayIndex = this.displayIndex + 1 == 3 ? 0 : this.displayIndex + 1;this.initSwiperData(this.originIndex);//如果滑到最后一条,请求新数据this.num++console.log('num',this.num,this.originList.length)if (this.num + 5 >= this.originList.length) {this.getPageID()}}// 如果两者的差为-2或者1则是向前滑动else if (this.displayIndex - current == -2 || this.displayIndex - current == 1) {this.originIndex = this.originIndex - 1 == -1 ? originListLength - 1 : this.originIndex - 1;this.displayIndex = this.displayIndex - 1 == -1 ? 2 : this.displayIndex - 1;this.initSwiperData(this.originIndex);if (this.num > 0) {this.num--}}},initSwiperData(originIndex = this.originIndex) {// console.log(this.displayIndex,originIndex)// 0 0// 1 1// 2 2// 0 3// 1 4//源数据长度let originListLength = this.originList.length;let displayList = [];displayList[this.displayIndex] = this.originList[originIndex];displayList[this.displayIndex - 1 == -1 ? 2 : this.displayIndex - 1] = this.originList[originIndex - 1 == -1 ? originListLength - 1 : originIndex - 1];displayList[this.displayIndex + 1 == 3 ? 0 : this.displayIndex + 1] = this.originList[originIndex + 1 ==originListLength ? 0 : originIndex + 1];// console.log(originIndex, (originIndex - 1 == -1 ? originListLength - 1 : originIndex - 1), (originIndex +// 1 == originListLength ? 0 : originIndex + 1))// 0 9 1// 1 0 2// 2 1 3// 3 2 4// 4 3 5//刷新数据this.displaySwiperList = displayList;// console.log(this.displaySwiperList,this.originList)},}}
</script><style>swiper {width: 100%;background: #000}swiper-item {height: 100%;width: 100%}video {height: 96%;width: 100%}.video-text {position: absolute;margin-left: 32rpx;width: 580rpx;bottom: 200rpx;z-index: 9999;}.tips {width: 560rpx;font-size: 26rpx;color: #ffffff;}
</style>
小爱心效果
<!DOCTYPE html>
<html><head><title>点赞特效</title><style>body {margin: 0;padding: 0;overflow: hidden;}#heart {position: absolute;top: 50%;left: 50%;width: 100px;height: 100px;border-radius: 50%;background: red;transform: translate(-50%, -50%);animation: heartBeat 1s linear infinite;}@keyframes heartBeat {0% {transform: scale(1);}50% {transform: scale(1.2);}100% {transform: scale(1);}}</style>
</head><body><script src="https://cdn.jsdelivr.net/npm/jquery"></script><script>$(document).ready(function () {var hearts = ["❤️", "💛", "💙", "💚", "💜", "🧡"];$(document).click(function (e) {var x = e.pageX;var y = e.pageY;var heartIcon = $("<div>").addClass("heart").text(hearts[Math.floor(Math.random() * hearts.length)]);$(heartIcon).css({position: "absolute",top: y - 10,left: x - 10,color: "red",userSelect: "none",pointerEvents: "none"});$("body").append($(heartIcon));// 1000 是动画的持续时间$(heartIcon).animate({top: y - 100,opacity: 0}, 1000, function () {$(heartIcon).remove();});});});</script>
</body></html>
效果图:
也可以将其换成爱心图片:
<!DOCTYPE html>
<html><head><title>点赞特效</title><style>body {margin: 0;padding: 0;overflow: hidden;}#heart {position: absolute;top: 50%;left: 50%;width: 100px;height: 100px;border-radius: 50%;background: red;transform: translate(-50%, -50%);animation: heartBeat 1s linear infinite;}@keyframes heartBeat {0% {transform: scale(1);}50% {transform: scale(1.2);}100% {transform: scale(1);}}</style>
</head><body><script src="https://cdn.jsdelivr.net/npm/jquery"></script><script>$(document).ready(function () {var hearts = ["❤️", "💛", "💙", "💚", "💜", "🧡"];$(document).click(function (e) {var x = e.pageX;var y = e.pageY;// var heartIcon = $("<div>").addClass("heart").text(hearts[Math.floor(Math.random() * hearts.length)]);var heartIcon = $("<img>").addClass("heart").attr("src", "./hh.png")$(heartIcon).css({position: "absolute",top: y - 10,left: x - 10,color: "red",wight:"40px",height:"40px",userSelect: "none",pointerEvents: "none"});$("body").append($(heartIcon));// 1000 是动画的持续时间$(heartIcon).animate({top: y - 100,opacity: 0}, 1000, function () {$(heartIcon).remove();});});});</script>
</body></html>
效果图:
相关文章:

uniapp 开发之仿抖音,上下滑动切换视频、点击小爱心效果
效果图: 功能描述: 上下滑动视频,双击暂停,然后第一个视频再往上滑显示”已经滑到顶了“ 开始代码: 首先视频接口使用的公开的视频测试接口 开放API-2.0 官网展示 Swagger UI 接口文档 一…...
【C++设计模式】依赖倒转原则
2023年8月30日,周三上午 目录 概述含义举个简单的例子传统做法使用依赖倒转原则代码说明再举一个具体的例子以生活为例 概述 依赖倒转原则(Dependency Inversion Principle,DIP)是面向对象设计中的一个基本原则。 含义 高层模块不应该依赖低层模块,两者都应该依…...

浙江首例!金华银行基于完全国产自研数据库构建新一代核心系统
6 月 12 日,金华银行举行“星辉工程”核心项目群上线发布会,新一代核心系统部署在国产分布式数据库 OceanBase 上,实现系统的高可用、高性能、国产升级。据悉,这是浙江省首例基于完全国产自研数据库落地的银行核心系统。 金华银行…...
ASP.NET Core 中的 静态文件
Static Files Static Files 包括 HTML,CSS,图片,JavaScript,以及其他静态资源文件。 即网站本身的内容。 Static Files 服务 Static Files 保存在项目的 Web Root 目录,即 wwwroot 文件夹中。 而wwwroot目录是Conte…...

2023年天府杯——C 题:码头停靠问题
问题背景: 某个港口有多个不同类型的码头,可以停靠不同种类的船只。每 艘船只需要一定的时间来完成装卸货物等任务,并且每个码头有容量 限制和停靠时间限制。港口需要在保证收益的情况下,尽可能地提高 运营效率和降低成本。同…...

集丰照明|汽车美容店设计,装修色彩灯光搭配方法
正确处理好店面的空间设计。 店铺各个功能区设计要合理,衔接合理,这样既能提高员工的工作效率也能提高顾客的满意度。合理安排店铺的空间分配, 要给顾客一种舒适度,既不能让顾客感觉到过于拥挤,又不能浪费店铺的有限空…...

性能提升3-4倍!贝壳基于Flink + OceanBase的实时维表服务
作者介绍:肖赞,贝壳找房(北京)科技有限公司 OLAP 平台负责人,基础研发线大数据平台部架构师。 贝壳找房是中国最大的居住服务平台。作为居住产业数字化服务平台,贝壳致力于推进居住服务的产业数字化、智能…...

取数组中每个元素的最高位
1 题目 /*程序将一维数组a中N个元素的最高位取出,保存在一维数组b的对应位置。 程序运行结果为: a:82 756 71629 5 2034 b: 8 7 7 5 2 */ 2 思考 简单来说就是取一个数据的最高位。 一开始的笨方法没有办法判断数据的长度,后来…...
Docker一键部署Nacos
官方参考文档: https://nacos.io/zh-cn/docs/quick-start-docker.html 本人实践 一、创建数据库&数据表 使用sql脚本创建:https://github.com/alibaba/nacos/blob/master/config/src/main/resources/META-INF/nacos-db.sql 二、新建文件夹并赋权…...

【数学建模】-- 模糊综合评价
模糊综合评价(Fuzzy Comprehensive Evaluation)是一种用于处理不确定性和模糊性信息的决策分析方法。它通常用于解决复杂的多指标决策问题,其中各指标之间可能存在交叉影响和模糊性的情况。模糊综合评价通过将不确定性和模糊性量化࿰…...

Java 数据库改了一个字段, 前端传值后端接收为null问题解决
前端传值后端为null的原因可能有很多种,我遇到一个问题是,数据库修改了一个字段,前端传值了,但是后台一直接收为null值, 原因排查: 1、字段没有匹配上,数据库字段和前端字段传值不一致 2、大…...

lnmp架构-mysql1
1.MySQL数据库编译 make完之后是这样的 mysql 初始化 所有这种默认不在系统环境中的路径里 就这样加 这样就可以直接调用 不用输入路径调用 2.初始化 重置密码 3.mysql主从复制 配置master 配置slave 当master 端中还没有插入数据时 在server2 上配slave 此时master 还没进…...
Threadlocal在项目中的应用
ThreadLocal为每一线程提供一份单独的存储空间,具有线程隔离的作用 PageHelper.startPage()方法使用ThreadLocal来保存分页参数,保证线程安全性。PageHelper通过集成MyBatis的拦截器机制来实现对SQL语句的拦截和修改 项目中使用了ThreadLocal保存每个线程…...

个性化定制你的AI助手,AI指令提示词专家
『个性化定制你的AI助手』围观不如下场!需要学习AI指令提升能力的,精准输出想要内容的,快来订阅 javastarboy『AI指令保姆级拆解』合集! ▶️你是否尚未挖掘到 AI 的潜力? ▶️你是否经常遇到“答非所问”的“人工智障…...

mongodb聚合排序的一个巨坑
现象: mongodb cpu动不动要100%,如下图 分析原因: 查看慢日志发现,很多条这样的查询,一直未执行行完成,占用大量的CPU [{$match: {"tags.taskId": "64dae0a9deb52d2f9a1bd71e",grnty: …...

无涯教程-分类算法 - 随机森林
随机森林是一种监督学习算法,可用于分类和回归,但是,它主要用于分类问题,众所周知,森林由树木组成,更多树木意味着更坚固的森林。同样,随机森林算法在数据样本上创建决策树,然后从每…...
c#常见的排序算法
在C#中,常见的排序算法包括以下几种: 1. 冒泡排序(Bubble Sort):比较相邻的元素,如果顺序不对就交换它们,重复多次直到排序完成。 2. 插入排序(Insertion Sort)…...

Redis 持久化和发布订阅
一、持久化 Redis 是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失。所以 Redis 提供了持久化功能! 1.1、RDB(Redis DataBase) 1.1.1 …...

k8s使用ECK(2.4)形式部署elasticsearch+kibana-http协议
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、准备elasticsearch-cluster.yaml二、部署并测试总结 前言 之前写了eck2.4部署eskibana,默认的话是https协议的,这里写一个使用http…...

[maven]关于pom文件中的<relativePath>标签
关于pom文件中的<relativePath>标签 为什么子工程要使用relativePath准确的找到父工程pom.xml.因为本质继承就是pom的继承。父工程pom文件被子工程复用了标签。(可以说只要我在父工程定义了标签,子工程就可以没有,因为他继承过来了&…...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...

Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...

以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...