Uniapp、小程序中双向联动购物车列表实现详解

Uniapp、小程序中双向联动购物车列表实现详解

商品的双向联动列表在电商、外卖等小程序,App中是非常常见的,相较于点检实现列表切换的购物车,双向联动列表的交互更为便捷,用户体验更加,但是实现起来也较为复杂。
dcloud插件市场是有现成的插件的,是黄河爱浪同学写的,可以直接使用,下面主要介绍一下实现方法。
商品双向联动列表插件 地址:https://ext.dcloud.net.cn/plugin?id=707

在这里插入图片描述

一、实现思路

数据准备

  • 准备两组数据:一组为商品分类列表,另一组为各分类对应的商品列表。这些数据可以是静态的,也可以是通过API接口从服务器动态获取。

界面布局

  • 观察页面知道需要采用两个竖直滚动的scroll-view或类似组件。左侧展示商品分类列表,右侧展示商品列表。

双向联动

  • 左侧分类点击:为左侧每个分类绑定点击事件,当用户点击某一分类时,触发一个函数,该函数根据所选分类的ID,从商品数据中筛选出对应分类的所有商品,并更新右侧商品列表的数据显示。
  • 右侧商品滚动监听:在右侧商品列表的scroll-view上绑定滚动事件监听器。当用户滚动查看商品时,通过计算或直接匹配方法识别出当前可视区域的主要商品所属分类。
  • 自动高亮分类:根据滚动时识别出的分类,自动在左侧分类列表中高亮显示该分类。可以通过改变CSS类或直接更新数据来实现高亮效果。同时,可利用scroll-into-view属性让左侧分类列表自动滚动到对应分类,确保视觉上的联动。

二、实现步骤

1、页面准备

准备一个主体,内部分为左右两个scroll-view

<view class="scroll-panel" id="scroll-panel">
	<view class="list-box">
		<view class="left">
			<scroll-view scroll-y="true"  :scroll-with-animation="false">
			</scroll-view>
		</view>
		<view class="main">
			<scroll-view scroll-y="true" :scroll-with-animation="false">
			</scroll-view>
		</view>
	</view>
</view>
<style lang="scss" scoped>
.scroll-panel {
	flex-grow: 1;
	height: 0;
	width: 100%;
	overflow: hidden;
	margin-bottom: 140rpx;
}
.list-box {
	width: 100%;
	display: flex;
	flex-direction: row;
	flex-wrap: nowrap;
	justify-content: flex-start;
	align-items: flex-start;
	align-content: flex-start;
	font-size: 26rpx;

	.left {
		width: 25%;
		background-color: #f6f6f6;
		line-height: normal;
		box-sizing: border-box;
		font-size: 26rpx;

		.item {
			padding: 30rpx;
			position: relative;

			& + .item {
				margin-top: 1px;

				&::after {
					content: '';
					display: block;
					height: 0;
					border-top: #d6d6d6 solid 1px;
					width: 620upx;
					position: absolute;
					top: -1px;
					right: 0;
					transform: scaleY(0.5); /* 1px像素 */
				}
			}

			&.active {
				color: #3c9cff;
				background-color: #fff;
				position: relative;

				&::before {
					content: '';
					display: block;
					position: absolute;
					top: 0;
					left: 0;
					border-left: #3c9cff solid 4px;
					height: 100%;
					width: 0;
				}
			}
		}

		.fill-last {
			height: 0;
			width: 100%;
			background: none;
		}
	}
	.main {
		width: 75%;
		background-color: #fff;
		padding-left: 20rpx;
		width: 0;
		flex-grow: 1;
		box-sizing: border-box;

		.title {
			line-height: normal;
			padding: 30rpx 0;
			font-size: 24rpx;
			font-weight: bold;
			color: #666;
			background-color: #fff;
			position: sticky;
			top: 0;
			z-index: 19;
		}

		.item {
			padding-bottom: 16rpx;
			border-bottom: #eee solid 1px;
		}

		.goods {
			width: 100%;
			display: flex;
			flex-direction: row;
			flex-wrap: nowrap;
			justify-content: flex-start;
			align-items: center;
			align-content: center;

			& + .goods {
				margin-top: 16rpx;
			}

			& > image {
				width: 120rpx;
				height: 120rpx;
				margin-right: 16rpx;
				margin-left: 2px;
			}
			.img {
				width: 36%;
				height: 94px;
				border-radius: 1px;
			}
		}
	}
}
</style>

2、数据准备

在实际应用场景中,左侧列表与右侧商品部分都是从后端接口请求获取的,因此在页面加载时,需要将数据准备好。

data() {
	return {
        scrollHeight: 400,//滑动区域高度
		scrollTopSize: 0,//滑动顶部高度
        fillHeight: 0, //填充高度
        leftIndex: 0,//左侧选中的序号
        scrollInto: '',//元素标记
        topArr: [],//每个元素距离顶部的位置
		leftArray: [],//左侧列表
		mainArray: [],//右侧商品
	}
},
onReady() {
    this.initScrollView().then(() => {
		//从接口请求列表数据
         //需要等待接口数据返回后,执行获取元素信息
        setTimeout(() => {
            this.getElementTop();
        }, 100);
	});
},
methods: {
    // 获取元素顶部信息
    async getElementTop() {
        const query = uni.createSelectorQuery();
        const mainItems = query.selectAll('.main-item');
        await mainItems.boundingClientRect();
        const { height: windowHeight } = await query.selectViewport().boundingClientRect();
        const data = await mainItems.exec();
        const topArr = data.map(item => item.top - this.scrollTopSize);
        this.topArr = topArr;
        const lastItemRect = data[data.length - 1];
        const padding = 20; // 给定一个常量来解释偏移量
        const lastItemBottomOffset = lastItemRect.height + lastItemRect.top - windowHeight + padding;
        // 动态设置填充高度
        this.fillHeight = lastItemBottomOffset > 0 ? windowHeight - lastItemBottomOffset : 0;
    },
    /* 初始化滚动区域 */
	initScrollView() {
		return new Promise((resolve, reject) => {
			let view = uni.createSelectorQuery().select('#scroll-panel');
			view.boundingClientRect((res) => {
				this.scrollTopSize = res.top;
				this.scrollHeight = res.height;
				setTimeout(() => {
					resolve();
				}, 100);
			}).exec();
		});
	},
},

3、左侧分类点击

为左侧每个分类绑定点击事件,当用户点击某一分类时,触发clickLeftTap函数,将右侧的滑动单元类型设置为当前点击分类的元素类型。

<view class="left">
	<scroll-view scroll-y="true" :scroll-into-view="leftIntoView" :scroll-with-animation="false">
		<view class="item" v-for="(item, index) in leftArray" :key="index" :class="{ active: index == leftIndex }" :id="'left-' + index" :data-index="index" @click="clickLeftTap">
		{{ item.category }}
		</view>
	</scroll-view>
</view>		
computed: {
	//计算左侧滚动位置定位
	leftIntoView() {
		return `left-${this.leftIndex > 3 ? this.leftIndex - 3 : 0}`;
	},
 },
methods: {
    //左侧导航点击
	clickLeftTap(e) {
		let index = e.currentTarget.dataset.index;
		this.scrollInto = `item-${index}`;
	}
}

4、右侧滚动事件

添加滑动监听事件mainScroll,计算当前显示元素与顶部的距离,超过则将下一个分类的标题展示出来,并且将左侧的leftIndex修改为当前右侧分类的index值,从而实现左侧分类的切换。

<view class="main">
	<scroll-view scroll-y="true" :style="{ height: scrollHeight + 'px' }" @scroll="mainScroll" :scroll-into-view="scrollInto" :scroll-with-animation="false">
        <view class="item main-item" v-for="(item, index) in mainArray" :key="index" :id="'item-' + index">
			<view class="title">
				<view>{{ item.title }}</view>
			</view>
		</view>
	</scroll-view>
</view>
methods: {
    mainScroll(e){
        // 节流方法
        clearTimeout(this.mainThrottle);
        this.mainThrottle = setTimeout(()=>{
            scrollFn();
        },10);
        let scrollFn = ()=>{
            let top =e.detail.scrollTop;
            let index=0;
            /* 查找当前滚动距离 */
            for(let i = (this.topArr.length-1);i>=0;i--){
                /* 在部分安卓设备上,因手机逻辑分辨率与rpx单位计算不是整数,滚动距离与有误差,增加2px来完善该问题 */
                if((top+2)>=this.topArr[i]){
                    index = i;
                    break;
                }
            }
            this.leftIndex=(index < 0 ? 0: index);
        }
    },
},

以上就是双向联动购物车列表的实现思路,如果对你有帮助请帮我点个关注 点个赞吧,谢谢!!!

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值