<template>
<view class="content">
<view class="head">Head fixing area</view>
<view class="list_box">
<!--Left to the menu-->
<view class="left">
<scroll-view scroll-y="true" class="scroll">
<view class="item" v-for="(item,index) in leftArray" :key="index"
:class="{ 'active':index==leftIndex }" :data-index="index" @tap="leftTap">{{item.id}}</view>
</scroll-view>
</view>
<view class="main">
<scroll-view scroll-y="true" @scroll="mainScroll" class="scroll" :scroll-into-view="scrollInto"
:scroll-with-animation="true" @touchstart="mainTouch" id="scroll-el">
<block v-for="(item,index) in mainArray" :key="index">
// This section shows that it changes according to your project requirements
// My horizontal scrolling is due to product requirements. Generally, it is listed as small icons and titles.
<scroll-view class="right-scroll" :scroll-x="true" :id="'item-'+index">
<block v-for="(item2,index2) in " :key="index2">
<view class="item">
<view class="goods">
<view>The left is the first{{ index + 1 }}indivual</view>
<view>On the right is the first{{ index2+1 }}indivual</view>
</view>
</view>
</block>
</scroll-view>
</block>
</scroll-view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
leftArray: [{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 4
},
{
id: 5
},
{
id: 6
},
{
id: 7
},
{
id: 8
},
],
mainArray: [],
topArr: [],
leftIndex: 0,
isMainScroll: false,
scrollInto: ''
}
},
mounted() {
this.getListData();
},
methods: {
/* Get list data */
getListData() {
/* Because there is no real data, the current method simulates data */
let [left, main] = [
[],
[]
];
for (let i = 0; i < 8; i++) {
left.push(`${i+1}Products`);
let list = [];
for (let j = 0; j < (i + 1); j++) {
list.push(j);
}
main.push({
title: `The${i+1}Product title`,
list
})
}
this.mainArray = main;
this.$nextTick(() => {
setTimeout(() => {
this.getElementTop();
}, 10)
});
},
//Get the height from the top
getScrollTop(selector) {
return new Promise((resolve, reject) => {
let query = uni.createSelectorQuery().in(this);
query.select(selector).boundingClientRect(data => {
resolve(data.top)
}).exec();
})
},
/* Get top information of the element */
async getElementTop() {
/* Promise object array */
let p_arr = [];
/* traverse the data and create the corresponding Promise array data */
for (let i = 0; i < this.mainArray.length; i++) {
const resu = await this.getScrollTop(`#item-${i}`)
p_arr.push(resu - 200)
}
/* Top distance of main area rolling container */
this.getScrollTop("#scroll-el").then((res) => {
let top = res;
// #ifdef H5
top += 43; // Due to the need for fixed prompt blocks, the default title bar of H5 is 44px
// #endif
/* Call this method after all node information is returned */
Promise.all(p_arr).then((data) => {
this.topArr = data;
});
})
},
/* Main area scroll monitoring */
mainScroll(e) {
if (!this.isMainScroll) {
return;
}
let top = e.detail.scrollTop;
let index = -1;
if (top >= this.topArr[this.topArr.length - 1]) {
index = this.topArr.length - 1;
} else {
index = this.topArr.findIndex((item, index) => {
return this.topArr[index + 1] >= top;
});
}
this.leftIndex = (index < 0 ? 0 : index);
},
/* Main area touch */
mainTouch() {
this.isMainScroll = true;
},
/* Click on the left navigation */
leftTap(e) {
let index = e.currentTarget.dataset.index;
this.isMainScroll = false;
this.leftIndex = Number(index);
this.scrollInto = `item-${index}`;
}
}
}
</script>
<style lang="scss" scoped>
.content {
.head {
width: 100%;
height: 400rpx;
background-color: aqua;
display: flex;
align-items: center;
justify-content: center;
}
.list_box {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: flex-start;
align-content: flex-start;
font-size: 28rpx;
height: calc(100vh - 400rpx);
.left {
width: 200rpx;
background-color: #f6f6f6;
line-height: 80rpx;
box-sizing: border-box;
font-size: 32rpx;
height: 100%;
.item {
padding-left: 20rpx;
position: relative;
&:not(:first-child) {
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);
}
}
&.active,
&:active {
color: #42b983;
background-color: #fff;
}
}
}
.main {
height: 100%;
background-color: #fff;
padding: 0 20rpx;
width: 0;
flex-grow: 1;
box-sizing: border-box;
.tips {
line-height: 64rpx;
font-size: 24rpx;
font-weight: bold;
color: #666;
height: 64rpx;
position: fixed;
top: 44px;
right: 0;
width: 530rpx;
z-index: 10;
background-color: #fff;
padding-left: 10rpx;
}
.right-scroll {
height: calc(100vh - 400rpx);
width: 100%;
background-color: #efba21;
border-bottom: 2rpx solid #fff;
/* Scroll horizontally */
white-space: nowrap;
flex-direction: row;
.item {
width: 100%;
height: 100%;
/* The outer layer of the item can be defined as an element inside the line before scrolling inline-block / inline-flex */
display: inline-flex;
.goods {
width: 100%;
height: 100%;
padding: 20rpx;
box-sizing: border-box;
background-color: #42b983;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
align-content: center;
margin-bottom: 10rpx;
border-right: 2rpx solid #fff;
}
.goods:last-child {
border-right: 0;
}
}
}
.right-scroll:last-child {
border-bottom: 0;
}
}
.scroll {
height: 100%;
}
}
}
</style>