uni-app中nvue如何制作侧滑菜单, 安卓机解决方案, 低端机型解决方案

2023-10-22 07:40

本文主要是介绍uni-app中nvue如何制作侧滑菜单, 安卓机解决方案, 低端机型解决方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近萌新制作的一个项目需要制作移动端应用, 为了减少开发成本首先想到了跨平台开发方式uni-app

uni-app一共有两种渲染方式:

  • 一种是写 .vue 最后以 web-view 渲染出页面, 这种模式因是基于浏览器所以很容易做到在iosAndroid页面保持一致, 但是这种模式有一个致命的缺点: 性能问题

  • 第二种是写 .nvue文件, 采用 week 技术渲染成原生组件, 这种模式性能没得话说, 但是因为局限于 week 本身的原因, 在有些方法很难做到iosAndroid页面保持一致

因为性能是我优先考虑的东西, 所以这里只能使用 nvue 的方式去开发, 在开发页面时一般会使用list 包裹页面元素从而达到高性能的滚动, 本文所讨论的问题就出现在这里: 我在DCloud插件市场查找到的侧滑菜单Android平台都存在同样的问题, 因为内存回收机制只有页面打开时可视部分能正常使用, 下面不可见的地方侧滑菜单都失效了, 下面让我们探索原因以及解决方案.

list

app端nvue专用组件。在app-nvue下,如果是长列表,使用list组件的性能高于使用viewscroll-view的滚动。原因在于list在不可见部分的渲染资源回收有特殊的优化处理。

Android 平台,因 <list> 高效内存回收机制,不在屏幕可见区域的组件不会被创建,导致一些内部需要计算宽高的组件无法正常工作 

 官网 list 组件文档https://uniapp.dcloud.io/component/listhttps://uniapp.dcloud.io/component/list

问题就出现在这里, Android平台不可见部分的计算宽度都失效了,导致侧滑失败. 下面我来向大家汇报我的解决方法, 以及遇到的问题.

先来看看我的页面 

截图于 iPhone xs max平台  

知道了失效原因, 那我们解决方式也很简单, 只要避免计算高度宽度就好了, 我将每一条聊天列表条目和侧滑菜单单独做成一个组件.

...
<cell v-for="(item,index) in chats"><chat-item :portrait="item.portrait":userName="item.userName":messages="item.messages":key="'chat-item-'+index":code="'chat-item-'+index":unread="item.unread":lastTime="item.lastTime"></chat-item>
</cell>
...

让我们看看自定义组件 <chat-item> 内长什么样子.

<!-- 聊天列表条目容器 -->
<div class="chat-container"><!-- 侧滑菜单 --><div class="chat-operate">...</div><!-- 聊天列表条目主体 --><div class="chat-item">...</div>
</div>
因为局限于week没有提供z-index属性问题, 没法正确的设置此属性, 聊天列表条目主体将来需要覆盖在侧滑菜单上面, 所以需要把聊天列表条目主体写在后面, 可以理解为越靠后z-index值越高.

week 通用样式http://doc.weex.io/zh/docs/styles/common-styles.htmlhttp://doc.weex.io/zh/docs/styles/common-styles.html

对于侧滑功能我们需要监听 聊天列表条目主体 (class="chat-item") 的手指事件, 这里需要监听的事件有3个:

  • @touchstart 手指按钮下触发
  • @touchmove 手指滑动触发
  • @touchend 手指离开屏幕触发
<template>
<!-- 聊天列表条目 -->
<div class="chat-container"><!-- 侧滑菜单 --><div class="chat-operate">...</div><!-- 主体部分 --><div class="chat-item" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd":style="chatItemStyle">...</div>
</div>
</template>
<script>
export default {computed: {// 主体部分位移大小chatItemStyle(){return {transform: `translateX(-${this.moveX})`}}},data(){return {// 主体部分手指落下的位置startX: 0,// 主体部分位移距离moveX: 0}},methods: {touchStart(e){// 判断是否存在该事件if (e.changedTouches.length == 1) {// 设置触摸起始点水平方向位置this.startX = e.changedTouches[0].pageXthis.startX += this.moveX;}},touchMove(e){if (e.changedTouches.length == 1) {// 手指移动时水平方向位置var moveX = e.changedTouches[0].pageX;// 手指起始点位置与移动期间的差值, 这里将值乘2是未了更方便打开侧滑动菜单var disX = (this.startX - moveX) * 2;// 赋值位移距离this.moveX = disX;}},touchEnd(e){if (e.changedTouches.length == 1) {// 手指移动结束后水平位置var endX = e.changedTouches[0].pageX;// 触摸开始与结束,手指移动的距离var disX = this.startX - endX;// 这里的为设置55为边界值, 如果当前手指松开时位移超过55会自动打开剩下的距离, 反之关闭侧滑菜单// 这里侧滑菜单的宽度为了避免计算必须设置固定值110if(disX > 55){// 打开this.moveX = 110}else{// 关闭this.moveX = 0}}}}
}
</script>
<style lang="scss" scoped>// 聊天列表条目容器.chat-container{	background-color: #FEFEFE;padding: 15rpx 20rpx;padding-bottom: 10rpx;position: relative;}// 聊天列表框.chat-item{flex-direction: row;align-items: stretch;border-radius: 20rpx;padding: 15rpx;background-color: #FEFEFE;transition-property: transform;transition-duration: .2s;transition-timing-function: ease;}// 菜单.chat-operate{position: absolute;width: 100px;height: 100rpx;top: 30rpx;right: 20rpx;flex-direction: row;justify-content: space-between;}
</style>

这里父容器为position: relative, 列表框一定不能position: absolute不然不能撑开条目的高度, 侧滑菜单可以为position: absolute但是不能存在宽度高度的计算. 

加上亿点点细节 

  1. 加入操作时背景颜色的提示
  2. 在打开过程滑动中阻止list滚动
  3. 打开一个侧滑菜单关闭其他条目的菜单

 Android低端机型或老式机型侧滑Bug


 

在一些Android低端机型老式机型可能存在侧滑Bug(侧滑卡在一半, 外部list没有接收到是否正常开关, 认为还在滑动所以无法滚动页面), 这是因为这些机型可能并不能正确的触发 @touchend 手指离开事件, 导致侧滑菜单计算失败. 这里的解决方案可以使用 @touchcancel 手指中断事件代替, 可以在组件初始化时 uni.getSystemInfo 检查操作系统版本.

 全部代码


chart-item.nvue 

<template><!-- 聊天列表 --><div class="chat-container"><div class="chat-operate"><!-- 置顶 --><div class="operate-top"><text class="chat-operate-icon operate-top-icon">&#xe61c;</text></div><!-- 删除 --><div class="operate-del"><text class="chat-operate-icon operate-del-icon">&#xe6c7;</text></div></div><!-- @touchcancel="touchEnd2" --><div :class="{'isMove':isMove}" class="chat-item" hover-class="none" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd" @touchcancel="touchEnd2"  :style="chatItemStyle"><!-- 	头像 --><div class="chat-portrait"><image class="chat-portrait-img" :src="portrait" mode="widthFix"></image></div><!-- 文字 --><div class="chat-message-group"><text class="chat-userName">{{userName}}</text><text class="chat-messages">{{messages}}</text></div><!-- 时间与小红点 --><div class="chat-info"><!-- 小红点 --><div class="info-tag" v-if="unread > 0"><text class="info-tag-text">{{unread | numberForMat}}</text></div><div v-if="unread <= 0"></div><!-- 时间 --><text class="info-time">{{lastTime | timeConversion}}</text></div></div></div>
</template><script>import { EventBus } from "../../unit/bus.js";export default {filters: {timeConversion(date){if(!new Date(date)) return '显示异常'var date = new Date(date);return (date.getHours() > 10 ? date.getHours() : '0' + date.getHours()) + ':' + (date.getMinutes() > 10 ? date.getMinutes() : "0" + date.getMinutes())},numberForMat(data){if(data > 999)return '999+'elsereturn data}},computed: {chatItemStyle(){return {transform: `translateX(-${this.moveX})`}}},props: {portrait: String,userName: String,messages: String,code: String,unread: Number,lastTime: [String,Number],},mounted() {this.init();EventBus.$on('chatItemOpen',(data)=>{if(data != this.code)this.moveX = 0});EventBus.$on('chatListScroll',()=>{this.moveX = 0this.startX = 0});},watch: {moveX: function(val){if(val > 0)this.isMove = trueelse if(val == 0)this.isMove = false}},methods: {init(){uni.getSystemInfo({success:(res) => {this.platform = res.platform}})},touchStart(e){if (e.changedTouches.length == 1) {// 设置触摸起始点水平方向位置this.startX = e.changedTouches[0].pageXthis.startX += this.moveX;}},touchMove(e){if (e.changedTouches.length == 1) {//手指移动时水平方向位置var moveX = e.changedTouches[0].pageX;// 手指起始点位置与移动期间的差值var disX = (this.startX - moveX) * 2;if(disX > 20){// if(this.platform == 'ios'){EventBus.$emit('chatItemMove',true);if(disX > 80){EventBus.$emit('chatItemMove',false);}if (disX == 0 || disX < 0) {disX = 0}else if(disX > 0){EventBus.$emit('chatItemOpen',this.code);if(disX >= 110){disX = 110 }}// }else{// 	if (disX == 0 || disX < 0) {// 		disX = 0// 	}else if(disX > 20){// 		EventBus.$emit('chatItemOpen',this.code);// 		if(disX >= 110){// 			disX = 110 // 		}// 	}// }this.moveX = disX;}}},touchEnd(e){// if(this.platform !== 'ios') return;if (e.changedTouches.length == 1) {EventBus.$emit('chatItemMove',false);// 手指移动结束后水平位置var endX = e.changedTouches[0].pageX;// 触摸开始与结束,手指移动的距离var disX = this.startX - endXif(disX > 55){// 打开this.moveX = 110}else{// 关闭this.moveX = 0}}},touchEnd2(){EventBus.$emit('chatItemMove',false);}// touchEnd2(e){// 	if(this.platform == 'ios') return;// 	if (e.changedTouches.length == 1) {// 		EventBus.$emit('chatItemMove',false);// 		// 手指移动结束后水平位置// 		var endX = e.changedTouches[0].pageX;// 		// 触摸开始与结束,手指移动的距离// 		var disX = this.startX - endX;// 		if(disX > 55){// 			// 打开// 			this.moveX = 110// 		}else{// 			// 关闭// 			this.moveX = 0// 		}// 	}// }},data(){return {startX: 0,moveX: 0,isMove: false,platform: ''}}}
</script><style lang="scss" scoped>
.chat-container{	background-color: #FEFEFE;padding: 15rpx 20rpx;padding-bottom: 10rpx;position: relative;
}
// 聊天列表框
.chat-item{flex-direction: row;align-items: stretch;border-radius: 20rpx;padding: 15rpx;background-color: #FEFEFE;transition-property: transform;transition-duration: .2s;transition-timing-function: ease;
}
// 正在移动
.chat-item.isMove{background-color: #EEEEEE;
}// 头像
.chat-portrait{width: 100rpx;height: 100rpx;border-radius: 20rpx;overflow: hidden;align-items: center;justify-content: center;
}
.chat-portrait-img{width: 100rpx;height: 100rpx;
}// 聊天
.chat-message-group{margin: 0 20rpx;justify-content: center;flex: 1;
}
.chat-userName{font-family: 'HarmonyOS_Sans_SC';color: $primaryText;font-size: 35rpx;font-weight: 600;
}
.chat-messages{font-family: 'HarmonyOS_Sans_SC';color: $regularText;font-size: 25rpx;font-weight: 400;flex: 1;text-overflow: ellipsis;overflow: hidden;lines: 1;
}
// 操作
.chat-operate{position: absolute;width: 100px;height: 100rpx;top: 30rpx;right: 20rpx;flex-direction: row;justify-content: space-between;
}
.operate-del{width: 45px;height: 100rpx;border-radius: 20rpx;align-items: center;justify-content: center;background-color: rgba($dangerColor,.3);
}
.chat-operate-icon{font-family: iconfont;font-size: 35rpx;
}
.operate-del-icon{color: $dangerColor;
}
.operate-top{width: 45px;height: 100rpx;border-radius: 20rpx;align-items: center;justify-content: center;background-color: rgba($warningColor,.3);
}
.operate-top-icon{color: $warningColor;font-size: 40rpx;
}
// 时间与小红点
.chat-info{justify-content: space-between;align-items: flex-end;
}
.info-tag{margin-top: 10rpx;background-color: #FE3B30;padding: 6rpx 12rpx;align-items: center;justify-content: center;border-radius: 20rpx;
}
.info-tag-text{color: #fff;line-height: 25rpx;font-size: 25rpx;font-weight: 500;font-family: 'HarmonyOS_Sans_SC';
}
.info-time{color: $regularText;font-size: 20rpx;font-family: 'HarmonyOS_Sans_SC';
}
</style>

 

message.vue

这是聊天列表页面, 这里的操作很简单, list 有一个属性可以控制本身是否开启滚动: scrollable, 监听子组件 chart-item 发出的 chatItemMove 滑动进行中事件控制 list 是否可以滚动.

...mounted() {// if(this.platform == 'ios'){// 监听子组件是否滑动进行中EventBus.$on('chatItemMove',(data)=>{// 滑动已结束, list可以滚动if(!data)this.scrollable = true// 滑动进行中, list禁止滚动elsethis.scrollable = false});// }
}...

 监听 list 的 @scroll 滚动触发事件, 当页面滚动时关闭所有侧滑菜单.

...methods: {listScroll(e){// if(this.platform == 'ios'){// 当页面发生滚动时向子组件发送chatListScroll事件, 以关闭所有侧滑菜单EventBus.$emit('chatListScroll');// }},
}...

 如果你对于使用uni-appnvue模式时面对多平台有更好的方式, 欢迎来讨论.

 

 

message.vue

 

 

这篇关于uni-app中nvue如何制作侧滑菜单, 安卓机解决方案, 低端机型解决方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/260027

相关文章

数据库oracle用户密码过期查询及解决方案

《数据库oracle用户密码过期查询及解决方案》:本文主要介绍如何处理ORACLE数据库用户密码过期和修改密码期限的问题,包括创建用户、赋予权限、修改密码、解锁用户和设置密码期限,文中通过代码介绍... 目录前言一、创建用户、赋予权限、修改密码、解锁用户和设置期限二、查询用户密码期限和过期后的修改1.查询用

深入理解Redis大key的危害及解决方案

《深入理解Redis大key的危害及解决方案》本文主要介绍了深入理解Redis大key的危害及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录一、背景二、什么是大key三、大key评价标准四、大key 产生的原因与场景五、大key影响与危

macOS怎么轻松更换App图标? Mac电脑图标更换指南

《macOS怎么轻松更换App图标?Mac电脑图标更换指南》想要给你的Mac电脑按照自己的喜好来更换App图标?其实非常简单,只需要两步就能搞定,下面我来详细讲解一下... 虽然 MACOS 的个性化定制选项已经「缩水」,不如早期版本那么丰富,www.chinasem.cn但我们仍然可以按照自己的喜好来更换

Xshell远程连接失败以及解决方案

《Xshell远程连接失败以及解决方案》本文介绍了在Windows11家庭版和CentOS系统中解决Xshell无法连接远程服务器问题的步骤,在Windows11家庭版中,需要通过设置添加SSH功能并... 目录一.问题描述二.原因分析及解决办法2.1添加ssh功能2.2 在Windows中开启ssh服务2

Redis连接失败:客户端IP不在白名单中的问题分析与解决方案

《Redis连接失败:客户端IP不在白名单中的问题分析与解决方案》在现代分布式系统中,Redis作为一种高性能的内存数据库,被广泛应用于缓存、消息队列、会话存储等场景,然而,在实际使用过程中,我们可能... 目录一、问题背景二、错误分析1. 错误信息解读2. 根本原因三、解决方案1. 将客户端IP添加到Re

python 字典d[k]中key不存在的解决方案

《python字典d[k]中key不存在的解决方案》本文主要介绍了在Python中处理字典键不存在时获取默认值的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录defaultdict:处理找不到的键的一个选择特殊方法__missing__有时候为了方便起见,

React实现原生APP切换效果

《React实现原生APP切换效果》最近需要使用Hybrid的方式开发一个APP,交互和原生APP相似并且需要IM通信,本文给大家介绍了使用React实现原生APP切换效果,文中通过代码示例讲解的非常... 目录背景需求概览技术栈实现步骤根据 react-router-dom 文档配置好路由添加过渡动画使用

Linux限制ip访问的解决方案

《Linux限制ip访问的解决方案》为了修复安全扫描中发现的漏洞,我们需要对某些服务设置访问限制,具体来说,就是要确保只有指定的内部IP地址能够访问这些服务,所以本文给大家介绍了Linux限制ip访问... 目录背景:解决方案:使用Firewalld防火墙规则验证方法深度了解防火墙逻辑应用场景与扩展背景:

SpringBoot嵌套事务详解及失效解决方案

《SpringBoot嵌套事务详解及失效解决方案》在复杂的业务场景中,嵌套事务可以帮助我们更加精细地控制数据的一致性,然而,在SpringBoot中,如果嵌套事务的配置不当,可能会导致事务不生效的问题... 目录什么是嵌套事务?嵌套事务失效的原因核心问题:嵌套事务的解决方案方案一:将嵌套事务方法提取到独立类

使用Python制作一个PDF批量加密工具

《使用Python制作一个PDF批量加密工具》PDF批量加密‌是一种保护PDF文件安全性的方法,通过为多个PDF文件设置相同的密码,防止未经授权的用户访问这些文件,下面我们来看看如何使用Python制... 目录1.简介2.运行效果3.相关源码1.简介一个python写的PDF批量加密工具。PDF批量加密