思路:
1.首先让两块区域各自滑动起来
2.当点击左边的item,右边对应的item就要滑到顶部。这时候就要获取到右边每个item到顶部的距离。然后再减掉不需要的元素位置。让scroll-top=该item距离顶部的位置
3.让左边菜单栏,每次点击的item都显示在中间。那么就要通过计算左边每个item的高度和整个左边菜单栏的高度并进行计算
4.在右边滚动时要进行监听,滚动到某个位置之后左边也要对应上所以就要调用对应的方法
<template>
<view class="u-warp">
<!-- 搜索 -->
<view class="u-search-box">
<view class="u-search-inner">
<u-icon class="search-icon" name="search" color="#ccc" size="35"></u-icon>
<u-input type="text" placeholder="请输入搜索关键字" />
</view>
</view>
<!-- 内容 -->
<view class="u-content-box">
<!-- z左边 -->
<scroll-view
scroll-with-animation
scroll-y="true" //开启滑动
:scroll-top="LeftScrollTop" //动态设置滑动的位置在那
:scroll-into-view="itemId" //没作用,可用于测试
class="left-scroll menu-scroll-view">
<view @tap.stop='switchTab(index)' class="u-tab-item" :class="[current == index? 'tab-action' :'']"
v-for="(item,index) in data" :key='index'>
<text>{{item}}</text>
</view>
</scroll-view>
<!-- z右边 -->
<scroll-view
scroll-y="true"
:scroll-top="RightScrollTop"
scroll-with-animation
@scroll='rightScroll'
class="right-scroll">
<view class="page-view">
<view class="goods-item" :id="'item'+index" v-for="(item,index) in data" :key='index'>
<view class="goods-title">
<u-divider class="u-divider" color="#000" fontSize='35'
style="background-color: transparent;">{{item}}</u-divider>
</view>
<!-- 类目 -->
<view class="goods-category">
<view class="category" v-for="(item,index) in category" :key='index'>
{{item}}
</view>
</view>
<!-- 商品 -->
<view class="goods-details-box" v-for="(item,index) in 5" :key='index'>
<view class="goods-text-box">
<!-- <image src="" ></image> -->
<view class="img">
<image src="../../static/1.jpg" mode=""></image>
</view>
<view class="goods-text">
<text class="goods-text-text">收音机收音机收音机收音机收音机收音机</text>
<view class="num">
商品编号:<text>12345648</text>
</view>
</view>
</view>
<view class="price-box">
<view class="price">
结算价:¥420
</view>
<u-button size="mini" type="warning" shape="square">月落</u-button>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
data: ['热门推荐', '热门', '冷门', '主球', '篮球', '家电', '手机', '美剧'
, '菜单', '日常', '骑车', '房产', '日历', '地产', '火锅',
],
category: ['全部', '常用品', '常用品', '常用品'],
current: 0,
RightScrollTop: '', //右边的scroll位置
LeftScrollTop: 0, //左边的菜单的初始滚动位置
LeftItemHight: 0, //左边菜单item的高度 Lefthight
LeftHight: 0, //左边菜单的高度
oldRightScrollTop: 0, //右边滑动时记录的旧位置
itemId: '', // 栏目右边scroll-view用于滚动的id
RightTopArr: [], //右边菜单每个item距离top的高度
RightHight: 0, //右边菜单的高度
time: null, //定时器
};
},
onReady() {
this.getRightItemTop();
},
methods: {
// 点击左边的item
async switchTab(index) {
if (this.RightTopArr.length == 0) {
await this.getRightItemTop();
}
if (index == this.current) return;
this.LeftScrollTop = this.oldRightScrollTop;
// 试图更新没有那么快,所以要用异步的$nextTick
this.$nextTick(function() {
this.RightScrollTop = this.RightTopArr[index];
this.current = index;
this.leftMenuStatus(index);
})
},
// 获取一个目标元素的高度,相当于jq中的height
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();
})
},
// 设置左边菜单的滚动状态,让item居中显示
async leftMenuStatus(index) {
this.current = index;
// 如果为0,意味着尚未初始化
if (this.LeftHight == 0 || this.LeftItemHight == 0) {
await this.getElRect('menu-scroll-view', 'LeftHight');
await this.getElRect('u-tab-item', 'LeftItemHight');
}
// 将菜单活动item垂直居中
// 先计算真个left一共多高,然后除以2就行了
this.LeftScrollTop = index * this.LeftItemHight + this.LeftItemHight / 2 - this.LeftHight / 2;
},
// 获取右边菜单的每个item距离顶部的位置用一个数组来接收
getRightItemTop() {
// 异步操作
new Promise(resolve => {
let selectorQuery = uni.createSelectorQuery();
// 获取节点的位置信息
selectorQuery.selectAll('.goods-item').boundingClientRect((rects) => {
// 如果节点尚未生成,rects值为[](因为用selectAll,所以返回的是数组),循环调用执行
if (!rects.length) {
setTimeout(() => {
this.getRightItemTop();
}, 10);
return;
}
// 生成之后开始添加进去数组
rects.forEach((rect) => {
// 这里减去rects[0].top,是因为第一项顶部可能不是贴到导航栏(比如有个搜索框的情况)
this.RightTopArr.push(rect.top - rects[0].top);
resolve();
})
}).exec()
})
},
// 获取右边的状态
async rightScroll(e) {
this.oldRightScrollTop = e.detail.scrollTop;
if (this.RightTopArr.length == 0) {
await this.getRightItemTop();
}
if (this.timer) return;
if (!this.LeftHight) {
await this.getElRect('menu-scroll-view', 'LeftHight');
}
setTimeout(() => { // 节流
this.timer = null;
// scrollHeight为右边菜单垂直中点位置
let scrollHeight = e.detail.scrollTop + this.LeftHight / 2;
for (let i = 0; i < this.RightTopArr.length; i++) {
let height1 = this.RightTopArr[i];
let height2 = this.RightTopArr[i + 1];
// 如果不存在height2,意味着数据循环已经到了最后一个,设置左边菜单为最后一项即可
if (!height2 || scrollHeight >= height1 && scrollHeight < height2) {
this.leftMenuStatus(i);
return;
}
}
}, 10)
},
// 相交
async observer() {
this.data.map((val, index) => {
let observer = uni.createIntersectionObserver(this);
// 检测右边scroll-view的id为itemxx的元素与right-box的相交状态
// 如果跟.right-box底部相交,就动态设置左边栏目的活动状态
observer.relativeTo('.right-scroll', {
top: 0
}).observe('#item' + index, res => {
if (res.intersectionRatio > 0) {
let id = res.id.substring(4);
this.leftMenuStatus(id);
}
})
})
},
}
}
</script>
<style lang="scss">
.u-warp {
height: calc(100vh);
/* #ifdef H5 */
height: calc(100vh - var(--window-top));
/* #endif */
display: flex;
flex-direction: column;
background-color: #F6F6F6;
.u-search-box {
margin-top: 10rpx;
.u-search-inner {
border-radius: 30rpx;
display: flex;
justify-content: center;
flex-direction: row;
align-items: center;
width: 80%;
margin: 0 auto;
border: 1px solid #007AFF;
height: 60rpx;
padding: 3rpx;
.search-icon {
margin: 0 10rpx;
}
}
}
.u-content-box {
margin-top: 40rpx;
display: flex;
flex: 1;
overflow: hidden; //1
.left-scroll {
background-color: #EEEEEE;
width: 200rpx;
// text-align: center;
font-size: 35rpx;
display: inline-block;
height: 100%;
.u-tab-item {
display: flex;
align-items: center;
justify-content: center;
height: 100rpx;
font-size: 26rpx;
text {}
}
.tab-action {
color: #E50A0B;
background-color: #F6F6F6;
font-weight: 600;
&::before {
content: '';
display: block;
background-color: #E50A0B;
width: 10rpx;
height: 50rpx;
position: absolute;
left: 0;
}
}
}
.right-scroll {
margin-left: 20rpx;
display: inline-block;
// background-color: #F29100;
.page-view {
.goods-item {
.goods-title {
.u-divider {
font-weight: 600;
}
}
.goods-category {
display: flex;
flex-direction: row;
align-items: center;
text-align: center;
justify-content: space-around;
white-space: nowrap;
overflow-x: scroll;
overflow-y: hidden;
flex-wrap: wrap;
.category {
background-color: #FFFFFF;
width: 100rpx;
height: 50rpx;
border-radius: 10rpx;
display: flex;
justify-content: center;
align-items: center;
// flex-wrap: wrap;
}
}
.goods-details-box {
background-color: white;
margin-top: 20rpx;
margin-right: 20rpx;
padding: 10rpx;
border-radius: 20rpx;
.goods-text-box {
display: flex;
flex-direction: row;
.img {
width: 150rpx;
height: 150rpx;
background-color: #C0C0C0;
image {
width: 110%;
height: 100%;
}
}
.goods-text {
margin-left: 10rpx;
.goods-text-text {
font-weight: 600;
font-size: 30rpx;
}
.num {
font-weight: 600;
margin-top: 20rpx;
text {
color: #ccc;
}
}
}
}
.price-box {
display: flex;
flex-direction: row;
margin-top: 40rpx;
.price {
font-size: 36rpx;
color: #F95659;
}
}
}
}
}
}
}
}
</style>
遇到的问题需要自己去检查元素,逻辑哪里错了。
1.scroll-view无法滚动,检查一下元素父元素是否设置了宽高,子元素是否排成竖排或者横排,是否设置了overflow
2.设置了伸缩盒子之后要注意一下设置宽高防止变形