1.html代码:
<template>
<view class="classicsBox">
<scroll-view class="classics-left" scroll-y="true" scroll-with-animation :scroll-into-view="clickId">
<view v-for="(item,index) in contentData" :key="item.id" :id="item.id" class="classics-left-item" :class="picked==index?'checkedStyle':''" @click="selectActiveEvt(item)">
{{item.title}}
</view>
</scroll-view>
<!-- :scroll-top="scrollRightTop" -->
<scroll-view scroll-y="true" :scroll-into-view="clickId" :scroll-anchoring="true" scroll-with-animation class="classics-right" @scroll="scrollEvt">
<view class="classics-right-item" v-for="item in contentData" :key="item.id" :id="item.id">
<view class="title">
{{item.title}}
</view>
<view class="item-box" v-for="it in item.content">
<img class="item-box-left" :src="it.img" alt="">
<view class="item-box-right">
<view class="item-box-right-name">
{{it.name}}
</view>
<view class="item-box-right-describe">
{{it.desc}}
</view>
<view class="item-box-right-buy">
<view class="item-box-right-price">
¥{{it.price}}
</view>
<view class="item-box-right-pick">
选规格
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
2.script代码
<script>
export default {
components: {},
data() {
return {
contentData:[{
id:'tab1',
title:'热销专区',
content:[
{id:101,img:'static/rexiao1.png',name:'郁金香',desc:'花语',price:'99',},
{id:102,img:'static/rexiao2.png',name:'郁金香',desc:'花语',price:'99',},
]
},
{
id:'tab2',
title:'生日鲜花',
content:[
{id:201,img:'static/shengri1.png',name:'郁金香',desc:'花语',price:'99',},
{id:202,img:'static/shengri2.png',name:'郁金香',desc:'花语',price:'99',},
]
},......],//列表数据
clickId:'tab1', //点击选项的id
picked:0, // 左侧选中选项的index
nowRightIndex:0, // 右边当前滚动的index
itemArr:[], //用于存放右侧item位置数据
scrollRightTop:0,
timer:null,
}
},
methods: {
// 左侧切换点击事件
selectActiveEvt(e) {
this.clickId = e.id;
this.picked = this.contentData.findIndex(it=>it.id==e.id);
},
scrollEvt(e){
// 防抖
if(this.timer){
clearTimeout(this.timer);
}
this.timer = setTimeout(()=>{
this.nowRightIndex = this.itemArr.findLastIndex(it=>e.detail.scrollTop>=(it.bottom-228))+1; //判断当前顶部是处于哪个item,获取当前item的index
if(this.nowRightIndex==-1) this.nowRightIndex=0;
if(this.nowRightIndex==this.picked) return;
this.clickId = this.contentData[this.nowRightIndex].id;
this.picked = this.nowRightIndex;
},500);
},
// 计算右侧每个item到顶部的距离,存放到数组
getItemDistence(){
new Promise(resolve=>{
let selectQuery = uni.createSelectorQuery().in(this);
selectQuery.selectAll('.classics-right-item').boundingClientRect(rect=>{
if(!rect.length){
setTimeout(()=>{
this.getItemDistence();
},10);
return;
}
rect.forEach(it=>{
this.itemArr.push(it); // 这里获取到的数据是每个item距离页面顶部的数据,以及每个item的自身数据
resolve();
})
}).exec()
})
},
},
mounted(){
// 设置一个延时,确保所有dom和样式加载完成,否则拿到的数据可能有误
setTimeout(()=>{
this.getItemDistence();
},500)
},
}
</script>
3.css样式
.classicsBox{
box-sizing: border-box;
width: 100%;
height: 100%;
display: flex;
padding-top: 8px;
}
.classics-left{
width: 25%;
height: 100%;
box-sizing: border-box;
padding-right: 6px;
/* overflow-y: auto; */
}
.classics-left-item{
height: 60px;
box-sizing: border-box;
text-align: center;
line-height: 60px;
color: black;
font-weight: 600;
font-size: 14px;
}
.classics-left-item:last-child{
border: none;
}
.classics-right{
background-color: #fff;
width: 75%;
box-sizing: border-box;
/* overflow-y: auto; */
}
.classics-right-item{
width: 100%;
box-sizing: border-box;
}
.title{
height: 50px;
line-height: 50px;
font-weight: 600;
font-size: 14px;
background: url('../../../static/itembackg.png') center center no-repeat ;
background-size: 100%;
box-sizing: border-box;
padding-left: 10px;
color: #87ff07;
font-family: 'Courier New', Courier, monospace;
/* -webkit-text-stroke: 1px #ff55ff; */
}
.item-box{
display: flex;
width: 100%;
box-sizing: border-box;
padding-bottom: 20px;
padding-left: 10px;
}
.item-box-left{
width: 90px;
height: 90px;
background-color: #fafafa;
border-radius: 8px;
box-sizing: border-box;
}
.item-box-right{
width: calc(100% - 102px);
box-sizing: border-box;
padding-left: 12px;
position: relative;
box-sizing: border-box;
background-color: #fff;
}
.item-box-right-name,.item-box-right-price{
color: black;
font-weight: 600;
font-size: 14px;
}
.item-box-right-describe{
color: #909090;
font-size: 12px;
}
.item-box-right-buy{
width: 100%;
position: absolute;
bottom: 0;
display: flex;
justify-content: space-between;
box-sizing: border-box;
padding-right: 12px;
}
.item-box-right-pick{
background: linear-gradient(#ffa7b7,#ffcee4);
padding: 4px 6px;
border-radius: 12px;
color: #ffffff;
font-size: 12px;
}
.checkedStyle{
background-color: #ffffff;
border-left: 5px solid #ffb3c8;
box-sizing: border-box;
color: #ff797b;
border-radius: 0 20px 20px 0;
}