web123456

Uniapp realizes left and right scroll linkage through scroll-view

<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>