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);
}
},
},
以上就是双向联动购物车列表的实现思路,如果对你有帮助请帮我点个关注 点个赞吧,谢谢!!!