19_微信小程序之优雅实现侧滑菜单

这篇具有很好参考价值的文章主要介绍了19_微信小程序之优雅实现侧滑菜单。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

19_微信小程序之优雅实现侧滑菜单

一.先上效果图

微信小程序侧滑菜单,微信小程序,微信小程序,前端,vue.js,移动开发,侧滑菜单

要实现这样一个效果,布局其实很简单,整体布局是一个横向滚动的scroll-view,难点在于怎么控制侧滑菜单的回弹,以及寻找回弹的边界条件? 此篇文章主要是基于uni-app来实现的,以后也将继续使用uni-app,但是即使使用的是原生微信小程序框架也不影响,思路都是一样的,而且uni-app的api和原生微信小程序api是对标的。

二.整体布局实现

整体布局是一个横向滚动的scroll-view,scroll-view内部有两个标签,第一个标签是内容区域,宽度占满组件的宽度,高度自适应,第二个标签用于摆放侧滑按钮,宽度为每一个侧滑按钮的宽度之和,由于是自定义组件,所以预留了slot插槽。

<template>
	<scroll-view class="swipe-cell__inner" enable-flex scroll-x>
		<view class="swipe-cell__content">
			<slot></slot>
		</view>
		<view class="swipe-cell__right">
			<slot name="right"></slot>
		</view>
	</scroll-view>
</template>

<script>
	export default {
		name:"swipe-cell",
		data() {
			return {
				
			};
		}
	}
</script>

<style scoped>
	.swipe-cell__inner /deep/ ::-webkit-scrollbar {
		display: block;
		width: 0px !important;
		height: 0px !important;
	}
	
	.swipe-cell__inner {
		width: 100%;
		display: inline-flex;
		flex-direction: row;
		align-items: flex-start;
		white-space: nowrap;
	}
	
	.swipe-cell__content {
		display: inline-block;
		width: 100%;
		flex-shrink: 0;
		position: relative;
		white-space: normal;
		overflow: hidden;
	}
	
	.swipe-cell__right {
		align-self: stretch;
		display: inline-flex;
		flex-direction: row;
		align-items: stretch;
		position: relative;
		white-space: normal;
	}
</style>
三.在页面中使用该组件

我们先把组件引入使用,页面布局没问题之后,再来考虑侧滑面板的回弹效果。

<template>
	<scroll-view class="scroll-view" scroll-y>
		<swipe-cell class="swipe-cell" v-for="(item, index) in 3">
			<view class="user-item">
				<image class="user-avatar" src="/static/avatar.png"/>
				<view class="user-info-group">
					<view class="user-name">andr_gale</view>
					<view class="user-desc clamp1">不管做什么事,必定有人赞成有人反对,因为大家重视的东西都有所不同。而且,不管什么事情,都可以随意给他加上好与不好的理由,所以,若果一定要分清争议与罪恶的行为,反而有问题。因此,重要的事情由心去决定就行了,不是凭感情,而是凭心</view>
				</view>
			</view>
			
			<template #right>
				<view class="user-button-group">
					<view class="user-button-follow">关注</view>
					<view class="user-button-chat">私信</view>
				</view>
			</template>
		</swipe-cell>
	</scroll-view>
</template>

<script>
	export default {
		data() {
			return {
				
			}
		},
		onLoad() {

		},
		methods: {

		}
	}
</script>

<style>
	page {
		width: 100%;
		height: 100%;
		overflow: hidden;
		display: flex;
		flex-direction: column;
		align-items: stretch;
	}
	
	.clamp1 {
		display: -webkit-box;
		word-break: break-all;
		overflow: hidden;
		-webkit-box-orient: vertical;
		text-overflow: ellipsis;
		-webkit-line-clamp: 1;
	}
	
	.clamp2 {
		display: -webkit-box;
		word-break: break-all;
		overflow: hidden;
		-webkit-box-orient: vertical;
		text-overflow: ellipsis;
		-webkit-line-clamp: 2;
	}
	
	.clamp3 {
		display: -webkit-box;
		word-break: break-all;
		overflow: hidden;
		-webkit-box-orient: vertical;
		text-overflow: ellipsis;
		-webkit-line-clamp: 3;
	}
	
	.scroll-view {
		height: 100%;
	}
	
	.swipe-cell {
		display: block;
		border-bottom: thin solid #f2f2f2;
	}
	
	.swipe-cell:last-child {
		border-bottom: none;
	}
	
	.user-item {
		display: flex;
		flex-direction: row;
		align-items: stretch;
		padding: 20rpx;
		box-sizing: border-box;
	}
	
	.user-avatar {
		width: 100rpx;
		height: 100rpx;
		display: block;
		border-radius: 50rpx;
		box-sizing: border-box;
		overflow: hidden;
	}
	
	.user-info-group {
		flex: 1;
		overflow: hidden;
		margin-left: 20rpx;
		display: flex;
		flex-direction: column;
		align-items: stretch;
		justify-content: center;
	}
	
	.user-name {
		font-size: 32rpx;
		color: #000;
		line-height: 1;
		margin-bottom: auto;
	}
	
	.user-desc {
		font-size: 24rpx;
		color: #999;
		line-height: 1;
		margin-top: auto;
	}
	
	.user-button-group {
		display: flex;
		flex-direction: row;
		align-items: stretch;
	}
	
	.user-button-follow {
		width: 160rpx;
		background: orange;
		font-size: 28rpx;
		color: white;
		display: flex;
		flex-direction: row;
		align-items: center;
		justify-content: center;
	}
	
	.user-button-chat {
		width: 160rpx;
		background: #09BB07;
		font-size: 28rpx;
		color: white;
		display: flex;
		flex-direction: row;
		align-items: center;
		justify-content: center;
	}
</style>

微信小程序侧滑菜单,微信小程序,微信小程序,前端,vue.js,移动开发,侧滑菜单

四.什么时候向左回弹打开? 什么时候向右回弹关闭

微信小程序侧滑菜单,微信小程序,微信小程序,前端,vue.js,移动开发,侧滑菜单

上图中,红色区域是一条辅助线,被固定在了组件的最由边的位置,为了好演示,我把宽度设置的比较宽,正常1px就够了,蓝色区域为侧滑面板的右半边。

当我们滑动完,放手后,红色区域与蓝色区域相交,这时应该向左回弹打开。

当我们滑动完,放手后,红色区域与蓝色区域不相交,这时应该向右回弹关闭。

<template>
	<view class="swipe-cell__container">
		<scroll-view class="swipe-cell__inner" enable-flex scroll-x>
			<view class="swipe-cell__content">
				<slot></slot>
			</view>
			<view class="swipe-cell__right">
				<view class="swipe-cell__right-half"></view>
				<slot name="right"></slot>
			</view>
		</scroll-view>
		<view class="swipe-cell__guide-line"></view>
	</view>
</template>

<script>
	...
</script>

<style scoped>
	.swipe-cell__container {
		position: relative;
		width: 100%;
	}
	
	...
	
	.swipe-cell__right-half {
		position: absolute;
		left: 50%;
		top: 0;
		width: 50%;
		height: 100%;
		background: blue;
	}
	
	.swipe-cell__guide-line {
		position: absolute;
		width: 6px;
		top: 0;
		bottom: 0;
		right: 0;
		background: red;
	}
</style>

问题转化为怎么判断红色区域与蓝色区域相交,我们的主角正式登场,使用IntersectionObserver可监听两个或多个组件节点在布局位置上的相交状态。

  • IntersectionObserver wx.createIntersectionObserver(Object component, Object options)

    • 在页面中使用: IntersectionObserver wx.createIntersectionObserver(this, {observeAll: true}),observeAll为true可同时监听多个节点
    • 在自定义组件中使用:IntersectionObserver this.createIntersectionObserver({observeAll: true})
  • IntersectionObserver IntersectionObserver.relativeTo(string selector, Object margins)

    • 使用选择器指定一个节点,作为参照区域
  • IntersectionObserver.observe(string targetSelector, IntersectionObserver.observeCallback callback)

    • 指定目标节点的选择器并监听由relativeTo指定的参照区域与目标节点是否相交,由于我们需要监听多个video节点,所以这里的目标节点选择器我们使用class选择器即可。
    • 当目标节点与参照区域相交时,callback(res)返回的res中的intersectionRatio大于0
    • 当目标节点与参照区域不相交时,callback(res)返回的res中的intersectionRatio等于0
  • IntersectionObserver.disconnect()

    • 最后当页面或组件销毁的时候,需调用IntersectionObserver.disconnect()取消监听
<template>
	<view class="swipe-cell__container">
		<scroll-view class="swipe-cell__inner" enable-flex scroll-x @touchend="onTouchEnd($event)">
			<view class="swipe-cell__content">
				<slot></slot>
			</view>
			<view class="swipe-cell__right">
				<view id="observable" class="swipe-cell__right-half"></view>
				<slot name="right"></slot>
			</view>
		</scroll-view>
		<view id="guide-line" class="swipe-cell__guide-line"></view>
	</view>
</template>

<script>
	export default {
		name:"swipe-cell",
		data() {
			return {
				intersectionObserver: undefined,
				intersectionRatio: -1,
			};
		},
		mounted() {
			this.intersectionObserver = uni.createIntersectionObserver(this)
			this.intersectionObserver.relativeTo("#guide-line")
				.observe("#observable", (res) => {
					let intersectionRatio = res.intersectionRatio
					this.intersectionRatio = intersectionRatio
				})
		},
		unmounted() {
			this.intersectionObserver.disconnect()
		},
		methods: {
			onTouchEnd: function(event) {
				if(this.intersectionRatio > 0) {
					//红色区域与蓝色区域相交,向左回弹打开
					console.log("onTouchEnd", "向左回弹打开")
				} else {
					//红色区域与蓝色区域不相交,向右回弹关闭
					console.log("onTouchEnd", "向右回弹关闭")
				}
			}
		}
	}
</script>

<style scoped>
  ...
</style>
五.怎么控制回弹?

通过设置scroll-view的scroll-into-view属性的值,滚动到指定元素来控制回弹,并设置scroll-with-animation为true来开启自动滚动动画效果,我们给内容区域的标签指定id为content,给侧滑面板的标签指定id为right,那么:

当红色区域与蓝色区域相交时,设置scroll-into-view属性的值为right,向左回弹打开

当红色区域与蓝色区域不相交,设置scroll-into-view属性的值为content,向左回弹关闭

<template>
	<view class="swipe-cell__container">
		<scroll-view class="swipe-cell__inner" enable-flex scroll-x @touchend="onTouchEnd($event)" :scroll-into-view="scrollIntoView" scroll-with-animation>
			<view id="content" class="swipe-cell__content">
				<slot></slot>
			</view>
			<view id="right" class="swipe-cell__right">
				<view id="observable" class="swipe-cell__right-half"></view>
				<slot name="right"></slot>
			</view>
		</scroll-view>
		<view id="guide-line" class="swipe-cell__guide-line"></view>
	</view>
</template>

<script>
	export default {
		name:"swipe-cell",
		data() {
			return {
				intersectionObserver: undefined,
				intersectionRatio: -1,
				scrollIntoView: "content"
			};
		},
		...
		methods: {
			onTouchEnd: function(event) {
				if(this.intersectionRatio > 0) {
					//红色区域与蓝色区域相交,向左回弹打开
					console.log("onTouchEnd", "向左回弹打开")
					this.scrollIntoView = "right"
				} else {
					//红色区域与蓝色区域不相交,向右回弹关闭
					console.log("onTouchEnd", "向右回弹关闭")
					this.scrollIntoView = "content"
				}
			}
		}
	}
</script>

<style scoped>
  ...
</style>

微信小程序侧滑菜单,微信小程序,微信小程序,前端,vue.js,移动开发,侧滑菜单

仔细观察上图,我们回发现有一个问题,当我们两次放手,两次的相交状态从不相交到相交或者从相交到不相交时,能正常回弹,

从相交到相交或者从不相交到不相交时,则不会回弹,这是因为:

从相交到相交的过程中,scroll-into-view始终为right不变

从不相交到不相交的过程中,scroll-into-view始终为content不变

要解决这个问题,我们需要在每一次放手的时候,产生一个唯一的随机数,给内容区域的标签指定id为content- 随机数,给侧滑面板的标签指定 i d 为 r i g h t − {随机数},给侧滑面板的标签指定id为right- 随机数,给侧滑面板的标签指定idright{随机数},最后,如果是回弹打开,设置scroll-into-view的值为right- 随机数,如果是回弹关闭,设置 s c r o l l − i n t o − v i e w 的值为 c o n t e n t − {随机数},如果是回弹关闭,设置scroll-into-view的值为content- 随机数,如果是回弹关闭,设置scrollintoview的值为content{随机数}。

这个随机数的生成规则,你可以自己写算法实现,这里我用了一种比较巧妙的办法,那就是在事件触发的回调中,系统会给我们一个event对象,我们通过这个event对象的timeStamp属性,可以获取到事件触发的时间戳,这个时间戳必然是唯一的,因此event.timesSamp就可以作为这个唯一的随机数来使用。

<template>
	<view class="swipe-cell__container">
		<scroll-view class="swipe-cell__inner" enable-flex scroll-x @touchend="onTouchEnd($event)" :scroll-into-view="scrollIntoView" scroll-with-animation>
			<view :id="'content-' + random" class="swipe-cell__content">
				<slot></slot>
			</view>
			<view :id="'right-' + random" class="swipe-cell__right">
				<view id="observable" class="swipe-cell__right-half"></view>
				<slot name="right"></slot>
			</view>
		</scroll-view>
		<view id="guide-line" class="swipe-cell__guide-line"></view>
	</view>
</template>

<script>
	export default {
		name:"swipe-cell",
		data() {
			return {
				intersectionObserver: undefined,
				intersectionRatio: -1,
				scrollIntoView: "content-0",
				random: 0,
			};
		},
		...
		methods: {
			onTouchEnd: function(event) {
				this.random = event.timeStamp || 0
				if(this.intersectionRatio > 0) {
					//红色区域与蓝色区域相交,向左回弹打开
					console.log("onTouchEnd", "向左回弹打开")
					this.$nextTick(() => {
						this.scrollIntoView = "right-" + this.random
					})
				} else {
					//红色区域与蓝色区域不相交,向右回弹关闭
					console.log("onTouchEnd", "向右回弹关闭")
					this.$nextTick(() => {
						this.scrollIntoView = "content-" + this.random
					})
				}
			}
		}
	}
</script>

<style scoped>
  ...
</style>

微信小程序侧滑菜单,微信小程序,微信小程序,前端,vue.js,移动开发,侧滑菜单

最后,我们把辅助色块去掉,就大功告成了。

<style scoped>
  ...
  
  .swipe-cell__right-half {
		position: absolute;
		left: 50%;
		top: 0;
		width: 50%;
		height: 100%;
		z-index: -1;
	}
	
	.swipe-cell__guide-line {
		position: absolute;
		width: 1px;
		top: 0;
		bottom: 0;
		right: 0;
		z-index: -1;
	}
</style>

微信小程序侧滑菜单,微信小程序,微信小程序,前端,vue.js,移动开发,侧滑菜单文章来源地址https://www.toymoban.com/news/detail-698476.html

六.完整代码
<template>
	<view class="swipe-cell__container">
		<scroll-view class="swipe-cell__inner" enable-flex scroll-x @touchend="onTouchEnd($event)" :scroll-into-view="scrollIntoView" scroll-with-animation>
			<view :id="'content-' + random" class="swipe-cell__content">
				<slot></slot>
			</view>
			<view :id="'right-' + random" class="swipe-cell__right">
				<view id="observable" class="swipe-cell__right-half"></view>
				<slot name="right"></slot>
			</view>
		</scroll-view>
		<view id="guide-line" class="swipe-cell__guide-line"></view>
	</view>
</template>

<script>
	export default {
		name:"swipe-cell",
		data() {
			return {
				intersectionObserver: undefined,
				intersectionRatio: -1,
				scrollIntoView: "content-0",
				random: 0,
			};
		},
		mounted() {
			this.intersectionObserver = uni.createIntersectionObserver(this)
			this.intersectionObserver.relativeTo("#guide-line")
				.observe("#observable", (res) => {
					let intersectionRatio = res.intersectionRatio
					this.intersectionRatio = intersectionRatio
				})
		},
		unmounted() {
			this.intersectionObserver.disconnect()
		},
		methods: {
			onTouchEnd: function(event) {
				this.random = event.timeStamp || 0
				if(this.intersectionRatio > 0) {
					//红色区域与蓝色区域相交,向左回弹打开
					console.log("onTouchEnd", "向左回弹打开")
					this.$nextTick(() => {
						this.scrollIntoView = "right-" + this.random
					})
				} else {
					//红色区域与蓝色区域不相交,向右回弹关闭
					console.log("onTouchEnd", "向右回弹关闭")
					this.$nextTick(() => {
						this.scrollIntoView = "content-" + this.random
					})
				}
			}
		}
	}
</script>

<style scoped>
	.swipe-cell__container {
		position: relative;
		width: 100%;
	}
	
	.swipe-cell__inner /deep/ ::-webkit-scrollbar {
		display: block;
		width: 0px !important;
		height: 0px !important;
	}
	
	.swipe-cell__inner {
		width: 100%;
		display: inline-flex;
		flex-direction: row;
		align-items: flex-start;
		white-space: nowrap;
	}
	
	.swipe-cell__content {
		display: inline-block;
		width: 100%;
		flex-shrink: 0;
		position: relative;
		white-space: normal;
		overflow: hidden;
	}
	
	.swipe-cell__right {
		align-self: stretch;
		display: inline-flex;
		flex-direction: row;
		align-items: stretch;
		position: relative;
		white-space: normal;
	}
	
	.swipe-cell__right-half {
		position: absolute;
		left: 50%;
		top: 0;
		width: 50%;
		height: 100%;
		z-index: -1;
	}
	
	.swipe-cell__guide-line {
		position: absolute;
		width: 1px;
		top: 0;
		bottom: 0;
		right: 0;
		z-index: -1;
	}
</style>

到了这里,关于19_微信小程序之优雅实现侧滑菜单的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 微信小程序毕业设计作品成品(19)微信小程序高校校园食堂就餐预约系统设计与实现

    博主介绍: 《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者,CSDN博客专家,在线教育专家,CSDN钻石讲师;专注大学生毕业设计教育和辅导。 所有项目都配有从入门到精通的基础知识视频课程,免费 项目配有对应开发文档、开题报告、任务书、PPT、论文模版

    2024年02月08日
    浏览(51)
  • 优雅的实现微信小程序动态tabBar及组件中实现动态数据

    这段时间在开发一个微信小程序,有个需求是,小程序底部的tabBar导航实现动态化。就是根据用户角色不同,显示不同的导航。要实现动态导航首先要考虑的是如何把小程序中的原生导航更改为自定义导航。 一、根据官方文档所说,实现自定义导航首先要在app.js中配置好ta

    2024年02月12日
    浏览(35)
  • 优雅实现微信小程序动态tabBar,根据不同用户角色显示不同底部导航

    背景 在开发小程序过程中,有个需求是,小程序底部的tabBar需要根据不同用户角色显示不同底部导航。此时就需要用到自定义底部导航 custom-tab-bar。 上次发文是组合显示4个底部tabBar导航,很多小伙伴评论说组合超过5个怎么办。他们的需求总数超过5个了。 现在我在这里更新

    2024年02月14日
    浏览(26)
  • 微信小程序 uniapp 电商项目使用scroll-view实现左右菜单联动,点击菜单子分类联动对应商品

    最近写了个微信小程序项目,一开始不理解scroll-view用法,用的另外一种方法写的,虽然实现了效果,但是代码层面来说,不大合理,后来又通过努力,用scroll-view实现了效果。现写个文章做个记录,方便自己和大家学习记录。 效果图请看第一张。布局:左右布局,右边又分

    2024年02月14日
    浏览(58)
  • 前端实现微信支付(H5,微信小程序)

    通常一些电商项目使用到微信支付操作,所以简单地介绍一下微信支付的具体流程。 微信支付是微信内置微信浏览器(其他浏览器不支持)或者微信小程序的支付接口,主要负责用户对商家执行支付操作的流程。 例如常见的电商在下单环节,就需要通过使用微信支付接口,

    2024年02月08日
    浏览(46)
  • 微信小程序实现点赞功能(前端)

    可以通过以下代码实现点赞效果,且只能点赞一次: wxml wxss js skuList   点赞前   点赞后   重复点赞

    2024年02月11日
    浏览(45)
  • 微信小程序 api+前端实现生成分享海报

    1.先看效果图,点击分享海报按钮,然后弹出分享海报  2.前端代码 这里用的组件有vant组件库还有canvas_drawer(一个画布组件) canvas_drawer下载地址 https://github.com/kuckboy1994/mp_canvas_drawer 把 components 中的 canvasdrawer 拷贝到自己项目下,然后再app.json中引用就行了,如下 \\\"usingCompon

    2024年02月09日
    浏览(40)
  • uniapp微信小程序 --下拉菜单

    就是原生写这个本来就是一件很简单的事情,但是uniapp里面不支持selct,他封装了东西应该是,插件市场试了好几个也不太行。最后还是找到一个博主的写的很好,记录一下。 这是封装好的需要什么样式自己调整 使用 博主地址附上https://www.cnblogs.com/OrochiZ-/p/15910440.html

    2024年03月26日
    浏览(32)
  • 【微信小程序系列:三】前端实现微信支付与代扣签约

    微信支付 算是比较常见的功能了,这里主要说下整个流程中 前端负责 实现的一些方面,其实官方文档说得也很详细了,可以直接看官方文档。觉得文档比较复杂的话,可以直接看我写的~ 跳到官方文档。 微信支付: 理清流程最重要,以购买一件A商品为例子。主要说明前端

    2024年02月11日
    浏览(49)
  • 微信小程序生态13-微信公众号自定义菜单、个性化菜单配置

    微信小程序生态1-初识小程序 微信小程序生态2-创建一个微信小程序 微信小程序生态3-微信小程序登录流程设计 微信小程序生态4-扫普通二维码进入小程序、打开短链接进入小程序 微信小程序生态5-微信公众号扫码登录PC端网页 微信小程序生态6-微信公众号授权登录(适用于H

    2024年02月13日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包