项目地址:https://github.com/Creambb/linkScroll
最近通过做一个商城的项目实战学习小程序,目前市场上大部分的商城列表都使用了菜单的左右联动功能,因为自己想做好这个项目,所以在实现了以后还进行了一下优化,想记录下来。
话不多说,直接上代码。建议下载github上的项目,里面内容比较全,有图片,看得更清晰
效果图
wxml
<view class="contain">
<view id="header">
<!-- 这是我写的头部组件,可以忽略 -->
<header title="Bingo" />
</view>
<view id="searchBox" class="searchBox">
<view class="search">
<image src=""></image>
<input placeholder="搜索商品"></input>
<image src=""></image>
</view>
<button class="search-button">搜索</button>
</view>
<view class='main'>
<view class='left'>
<scroll-view scroll-y="true" class="leftScroll" scroll-into-view="true" scroll-with-animation="true">
<block wx:for="{{dataList}}" wx:for-list="item" wx:for-index="index" wx:key="index">
<view class="{{currentActiveIndex == index?'leftActive':'leftInactive'}}" data-index="{{index}}" data-id='{{item.id}}' bindtap='changeLeftMenu'>
<text>{{item.navTitle}}</text>
</view>
</block>
</scroll-view>
</view>
<view class='right'>
<!--
1. scroll-into-view="{{'inToview'+rigId}}"和下面的id="{{'inToview'+item.id}}"对应
2. rigId为变量,设置rigId的值,列表会滑动到对应的id
3. 注意:scroll-into-view的属性不能以数字开头
-->
<scroll-view scroll-y="true" style="height: 1100rpx" bindscroll="scroll" scroll-top="{{scrollTop}}" scroll-into-view="{{'inToview'+rigId}}" scroll-with-animation="true">
<block wx:for="{{dataList}}" wx:for-list="item" wx:for-index="index" wx:key="index">
<view wx:if="{{item.goodsList.length != 0}}" class='itemTitle' id="{{'inToview'+item.id}}">{{item.navTitle}}</view>
<view class='listItem' data-id='{{item.id}}'>
<block wx:for="{{item.goodsList}}" wx:for-index="idx" wx:key="idx">
<view class='listItemSub' data-text="{{}}">
<view class='img'>
<!-- 图片出不来就用灰色块代替了 -->
<!-- <image src='{{item.goodsUrl}}'></image> -->
<image src=''></image>
</view>
<view class='listText'>
<text>{{item.goodsName}}</text>
<text class='money'>¥{{item.shopPrice}}</text>
<view>
<text>已售{{item.saleNum}}</text>
<image class="add-image" src="../../images/goods/add.svg"></image>
</view>
</view>
</view>
</block>
</view>
</block>
</scroll-view>
</view>
</view>
</view>
xcss
::-webkit-scrollbar {
width: 0;
height: 0;
color: transparent;
}
::-webkit-scrollbar-track {
height: 20rpx;
color: black;
}
page {
width: 100%;
height: 100%;
}
.contain {
width: 100%;
height: 100%;
box-sizing: border-box;
/* padding-top: 140rpx; */
padding-bottom: 100rpx;
}
.leftActive {
color: red;
background-color: #fff;
}
.leftInactive {
background-color: #F8F8F8;
}
.searchBox {
display: flex;
background-color: rgba(219, 219, 221, 1);
align-items: center;
height: 60rpx;
padding: 10rpx 0;
position: fixed;
left: 0;
width: 100%;
z-index: 1;
}
.searchBox .search-button {
/* width: auto; */
min-height: 50rpx;
line-height: 50rpx;
width: 15%;
padding: 0;
line-height: 1;
font-size: 30rpx;
}
.search {
display: flex;
justify-content: space-between;
align-items: center;
/* height: 50rpx; */
border-radius: 30rpx;
margin-left: 20rpx;
padding: 0 20rpx;
background-color: white;
width: 65%;
}
.search input {
font-size: 30rpx;
width: 100%;
}
.search text {
color: gainsboro;
font-family: monospace;
font-size: 30rpx;
line-height: 46rpx;
}
.search image {
padding: 0 5rpx;
width: 46rpx;
height: 46rpx;
}
.main {
display: flex;
padding-top: 80rpx;
}
.left {
width: 25%
}
.left view {
width: 180rpx;
height: 70rpx;
padding-top: 30rpx;
text-align: center;
font-size: 32rpx;
}
.right {
width: 75%;
}
.listItemSub {
display: flex;
}
.listItem .img {
width: 200rpx;
height: 200rpx;
text-align: center;
padding-top: 10rpx;
}
.listItem .img image {
width: 80%;
height: 80%;
background-color: #eee;
}
.itemTitle {
font-size: 32rpx;
padding-left: 20rpx;
padding-top: 10rpx;
color: gray;
}
.listItem .listText {
display: flex;
flex-direction: column;
margin-top: 6rpx;
width: calc(100% - 200rpx);
}
.listItem .listText view {
display: flex;
align-items: flex-end;
justify-content: space-between;
}
.listItem .listText text {
font-size: 34rpx;
margin-top: 10rpx;
}
.listItem .listText .money {
color: red;
}
.listItem .listText view text {
font-size: 28rpx;
color: silver;
margin-right: 60rpx;
/* add */
min-width: 80px;
display: inline-block;
}
.listItem .listText view button {
background-color: red;
color: white;
padding-right: 8px;
padding-left: 8px;
padding-top: 0px;
}
.add-image {
width: 40rpx;
height: 40rpx;
padding-right: 20rpx;
}
.leftScroll {
height: 100%;
width: 180rpx;
background: #F8F8F8;
}
js
Page({
data: {
// 数据列表
dataList: [
{
id: "01",
navTitle: "活动",
goodsList: [{
id: 1,
goodsUrl: "../../images/goods/pencil01.jpg",
goodsName: "晨光优品中性笔",
typeList: ['粉红色', '淡蓝色', '淡黄色'],
shopPrice: 3,
saleNum: 20,
},
{
id: 2,
goodsUrl: "../../images/goods/pencil02.jpg",
goodsName: "得力彩色中性笔",
typeList: ['白色', '橘色'],
shopPrice: 5.2,
saleNum: 20,
},
{
id: 3,
goodsUrl: "../../images/goods/pencil03.jpg",
goodsName: "得力糖果中性笔",
shopPrice: 12,
saleNum: 20,
},
{
id: 4,
goodsUrl: "../../images/goods/pencil04.jpg",
goodsName: "按压式花边修正带",
shopPrice: 6.9,
saleNum: 20,
},
{
id: 5,
goodsUrl: "../../images/goods/pencil05.jpg",
goodsName: "得力文具便利贴",
shopPrice: 4,
saleNum: 20,
}
],
},
{
id: "02",
navTitle: "新品",
goodsList: [{
id: 21,
goodsUrl: "../../images/goods/pencil01.jpg",
goodsName: "晨光优品中性笔",
shopPrice: 3,
saleNum: 20,
},
{
goodsUrl: "../../images/goods/pencil02.jpg",
goodsName: "得力彩色中性笔",
shopPrice: 5.2,
saleNum: 20,
id: 22,
},
{
id: 4,
goodsUrl: "../../images/goods/pencil04.jpg",
goodsName: "按压式花边修正带",
shopPrice: 6.9,
saleNum: 20,
},
{
id: 5,
goodsUrl: "../../images/goods/pencil05.jpg",
goodsName: "得力文具便利贴",
shopPrice: 4,
saleNum: 20,
}
],
},
{
id: "03",
navTitle: "推荐",
goodsList: [{
goodsUrl: "../../images/goods/pencil01.jpg",
goodsName: "晨光优品中性笔",
shopPrice: 3,
saleNum: 20,
id: 31,
},
{
goodsUrl: "../../images/goods/pencil02.jpg",
goodsName: "得力彩色中性笔",
shopPrice: 5.2,
saleNum: 20,
id: 32,
},
{
id: 4,
goodsUrl: "../../images/goods/pencil04.jpg",
goodsName: "按压式花边修正带",
shopPrice: 6.9,
saleNum: 20,
},
{
id: 5,
goodsUrl: "../../images/goods/pencil05.jpg",
goodsName: "得力文具便利贴",
shopPrice: 4,
saleNum: 20,
},
],
},
{
id: "04",
navTitle: "晨光系列",
goodsList: [{
goodsUrl: "../../images/goods/pencil01.jpg",
goodsName: "晨光优品中性笔",
shopPrice: 3,
saleNum: 20,
id: 41,
},
{
id: 4,
goodsUrl: "../../images/goods/pencil04.jpg",
goodsName: "晨光按压式花边修正带",
shopPrice: 6.9,
saleNum: 20,
},
{
id: 5,
goodsUrl: "../../images/goods/pencil05.jpg",
goodsName: "晨光文具便利贴",
shopPrice: 4,
saleNum: 20,
},
],
},
{
id: "05",
navTitle: "文具专区",
goodsList: [{
goodsUrl: "../../images/goods/pencil01.jpg",
goodsName: "晨光优品中性笔(美)",
shopPrice: 3,
saleNum: 20,
id: 61,
},
{
goodsUrl: "../../images/goods/pencil02.jpg",
goodsName: "得力彩色中性笔",
shopPrice: 5.2,
saleNum: 20,
id: 62,
},
],
},
{
id: "06",
navTitle: "精品专区",
goodsList: [{
goodsUrl: "../../images/goods/pencil01.jpg",
goodsName: "晨光优品中性笔",
shopPrice: 3,
saleNum: 20,
id: 1,
},
{
goodsUrl: "../../images/goods/pencil02.jpg",
goodsName: "得力彩色中性笔",
shopPrice: 5.2,
saleNum: 20,
id: 2,
},
],
},
],
searchHeight: 0, // 搜索框高度
currentActiveIndex: 0, // 当前菜单索引
isClickMenu: false, // 是否点击菜单
proListToTop: [], // 记录每一个类型的列表道顶部的距离
},
onLoad: function (options) {
this.getEleHeight();
},
getEleHeight() {
var searchHeight, proListToTop = [];
// 搜索框所占据的高度,在后续滑动时需要减掉该高度
wx.createSelectorQuery().select("#searchBox").boundingClientRect(res => {
searchHeight = res.height
// 回调函数是异步的,所以需要在方法里面更新数据
this.setData({
searchHeight: searchHeight,
})
}).exec()
// 记录每一个类型的列表道顶部的距离
wx.createSelectorQuery().selectAll('.itemTitle').boundingClientRect((rects) => {
rects.forEach((rect) => {
proListToTop.push(rect.top.toFixed(2) - this.data.searchHeight);
})
this.setData({
proListToTop: proListToTop,
})
}).exec()
},
//滚动触发
scroll: function (e) {
// 若滑动是点击菜单触发的,则不进行判断;
if (!this.data.isClickMenu) {
// 获取滚动的高度
var scrollTop = e.detail.scrollTop;
var proListToTop = this.data.proListToTop;
var currentActiveIndex;
for (let i = 0; i < proListToTop.length; i++) {
// 根据滑动距离判断当前处于哪个菜单索引
if (e.detail.scrollTop < proListToTop[i] && i !== 0 && e.detail.scrollTop > proListToTop[i - 1]) {
this.setData({
currentActiveIndex: i - 1,
})
}
}
}
// 将点击菜单状态还原为false
this.setData({
isClickMenu: false,
})
},
//点击左边菜单事件
changeLeftMenu: function (e) {
// 当前点击的导航对应的右列表id
var rigId = e.currentTarget.dataset.id;
// 当前导航索引
var index = e.currentTarget.dataset.index;
// 点击当前导航索引,不作处理
if (this.data.currentActiveIndex === index) {
return;
}
this.setData({
rigId: rigId,
// 设置选中id
currentActiveIndex: index,
// 是否点击
isClickMenu: true
})
},
})