uniapp实现仿京东左右联动活动(商品分类)

第一步,页面布局

<view class="u-wrap">
		<view class="u-menu-wrap">
			<scroll-view scroll-y scroll-with-animation class="u-tab-view menu-scroll-view" :scroll-top="scrollTop" :scroll-into-view="itemId">
				<view v-for="(item,index) in flist" :key="index" class="u-tab-item" :class="[current == index ? 'u-tab-item-active':'']" @tap.stop="swichMenu(index)">
					<text class="u-line-1">{{item.name}}</text>
				</view>
			</scroll-view>
			<scroll-view scroll-y scroll-with-animation :scroll-top="scrollRightTop" class="right-box" @scroll="rightScroll">
				<view class="page-view">
					<view class="class-item" :id="'item' + index" v-for="(item,index) in slist" :key="index">
						<view class="item-title">
							<text>{{item.name}}</text>
						</view>
						<view class="item-container">
							<view class="thumb-box" v-for="(item1,index1) in item.children" :key="index1" @click="featureC(item1.id)">
								<image :src="item1.img_url" class="item-menu-image" mode=""></image>
								<view class="item-menu-name">{{item1.name}}</view>
							</view>
						</view>
					</view>
					
				</view>
			</scroll-view>
		</view>
	</view>

第二步:css部分,这里就自己写了,按照自己的项目效果图自己调一下效果

第三步:js部分

1、

data() {
			return {
				scrollTop: 0, //tab标题的滚动条位置
				oldScrollTop: 0, // tab标题的滚动条旧位置
				current: 0, // 预设当前项的值
				menuHeight: 0, // 左边菜单的高度
				menuItemHeight: 0, // 左边菜单item的高度
				itemId: '', // 栏目右边scroll-view用于滚动的id
				scrollRightTop: 0, // 右边栏目scroll-view的滚动条高度
				arr: [], // 储存距离顶部高度的数组
				timer: null, // 定时器
				flist: [], //左侧一级分类
				slist: []  //右侧一级+二级分类,构造可以嵌套的数组结构

			}
		},

2、点击左侧分类事件

/**
			* 点击左边的栏目切换
			* @index 传入的 ID
			*/
			async swichMenu(index) {
				if(this.arr.length == 0) {
					await this.getMenuItemTop();
				}
				if (index == this.current) return;
				this.scrollRightTop = this.oldScrollTop;
				this.$nextTick(function(){
					this.scrollRightTop = this.arr[index];
					this.current = index;
					this.leftMenuStatus(index);
				})
			},

3、右侧滑动事件

 

async rightScroll(e) {
					this.oldScrollTop = e.detail.scrollTop;
					if(this.arr.length == 0) {
						await this.getMenuItemTop();
					}
					if(this.timer) return ;
					if(!this.menuHeight) {
						await this.getElRect('menu-scroll-view', 'menuHeight');
					}
					setTimeout(() => { // 节流
						this.timer = null;
						// scrollHeight为右边菜单垂直中点位置
						// let scrollHeight = e.detail.scrollTop + this.menuHeight / 2;
						// scrollHeight为右边菜单头部位置
						let scrollHeight = e.detail.scrollTop + 20;
						for (let i = 0; i < this.arr.length; i++) {
							let height1 = this.arr[i];
							let height2 = this.arr[i + 1];
							if (!height2 || scrollHeight >= height1 && scrollHeight < height2) {
								this.leftMenuStatus(i);
								return ;
							}
						}
					}, 10)
			},

4、页面初始化时,获取右边每个节点距离顶部的高度,主要用于判断是否滑动,比如右边数据比较少的时候就不会在滑动

onReady() {
	this.getMenuItemTop()	
},


getMenuItemTop() {
			  	new Promise(resolve => {
			  		let selectorQuery = uni.createSelectorQuery();
			  		selectorQuery.selectAll('.class-item').boundingClientRect((rects) => {
			  			// 如果节点尚未生成,rects值为[](因为用selectAll,所以返回的是数组),循环调用执行
			  			if(!rects.length) {
			  				setTimeout(() => {
			  					this.getMenuItemTop();
			  				}, 10);
			  				return ;
			  			}
			  			rects.forEach((rect) => {
			  				// 视情况而定,这里减去rects[0].top,是因为第一项顶部可能不是贴到导航栏(比如有个搜索框的情况)
			  				// this.arr.push(rect.top - rects[0].top);
			  				this.arr.push(rect.top)
			  				resolve();
			  			})
			  		}).exec()
			  	})
			},

5、基础功能函数

/**
			* 观测元素相交状态
			* 检测右边scroll-view的id为itemxx的元素与right-box的相交状态
			* 如果跟.right-box底部相交,就动态设置左边栏目的活动状态
			*/
			async observer() {
				this.tabbar.map((val, index) => {
					let observer = uni.createIntersectionObserver(this);
					observer.relativeTo('.right-box', {
						top: 0
					}).observe('#item' + index, res => {
						if (res.intersectionRatio > 0) {
							let id = res.id.substring(4);
							this.leftMenuStatus(id);
						}
					})
				})
			},
			/**
			* 设置左边菜单的滚动状态
			* @index 传入的 ID
			*/
			async leftMenuStatus(index) {
				this.current = index;
				// 如果为0,意味着尚未初始化
				if (this.menuHeight == 0 || this.menuItemHeight == 0) {
					await this.getElRect('menu-scroll-view', 'menuHeight');
					await this.getElRect('u-tab-item', 'menuItemHeight');
				}
				// 将菜单活动item垂直居中
				this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2;
			},

/**
			* 获取一个目标元素的高度
			* @elClass 元素的类名
			* @dataVal 储存高度的对象
			*/
			getElRect(elClass, dataVal) {
					new Promise((resolve, reject) => {
						const query = uni.createSelectorQuery().in(this);
						query.select('.' + elClass).fields({
							size: true
						}, res => {
							// 如果节点尚未生成,res值为null,循环调用执行
							if (!res) {
								setTimeout(() => {
									this.getElRect(elClass);
								}, 10);
								return;
							}
							this[dataVal] = res.height;
							resolve();
						}).exec();
					})
			},

主要是uniapp操作dom节点的时候不是很方便,核心的实现原理其实跟jq一样 

 

 

 

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个简单的示例代码,演示了如何在 uniapp实现分类左右联动效果: ```html <template> <view class="container"> <view class="left"> <!-- 左侧分类列表 --> <scroll-view scroll-y="true"> <view v-for="(category, index) in categories" :class="{ active: currentIndex === index }" @click="handleCategoryClick(index)"> {{ category }} </view> </scroll-view> </view> <view class="right"> <!-- 右侧商品列表 --> <scroll-view scroll-y="true"> <view v-for="(product, index) in products" :key="product.id"> <image :src="product.image" /> <view>{{ product.name }}</view> <view>¥{{ product.price }}</view> </view> </scroll-view> </view> </view> </template> <script> export default { data() { return { categories: ['水果', '蔬菜', '肉类', '海鲜'], // 左侧分类列表 products: [ // 右侧商品列表 { id: 1, name: '苹果', price: 5, image: 'https://example.com/apple.png', category: '水果', }, { id: 2, name: '香蕉', price: 3, image: 'https://example.com/banana.png', category: '水果', }, { id: 3, name: '西红柿', price: 2, image: 'https://example.com/tomato.png', category: '蔬菜', }, { id: 4, name: '鸡肉', price: 12, image: 'https://example.com/chicken.png', category: '肉类', }, { id: 5, name: '虾', price: 20, image: 'https://example.com/shrimp.png', category: '海鲜', }, ], currentIndex: 0, // 当前选中的分类索引 }; }, methods: { handleCategoryClick(index) { // 点击分类列表时更新当前选中的分类索引 this.currentIndex = index; }, }, }; </script> <style> .container { display: flex; flex-direction: row; height: 100%; } .left { width: 30%; height: 100%; background-color: #f2f2f2; padding: 10px; } .left .active { color: #007aff; font-weight: bold; } .right { flex: 1; height: 100%; padding: 10px; } .right image { width: 100%; height: 200px; object-fit: cover; } </style> ``` 在这个示例中,我们定义了一个 `categories` 数组作为左侧分类列表,以及一个 `products` 数组作为右侧商品列表。同时,我们使用了一个 `currentIndex` 变量来记录当前选中的分类索引。当用户点击左侧分类列表中的某个分类时,我们会更新 `currentIndex` 变量,然后根据当前选中的分类来过滤右侧商品列表中的商品。最后,我们使用了 `v-for` 指令来循环渲染左侧分类列表和右侧商品列表,并使用了 `class` 绑定来控制左侧选中的分类样式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值