這個圖分解成4塊 上左:樓層 上右:基礎數據 下左:固定文字 下右:樓棟
樓層可上下滑動
樓棟可左右滑動
基礎信息可上下左右滑動
樓層scroll-view 與基礎信息scroll-view 上下滑動關聯
樓棟scroll-view 與基礎信息scroll-view 左右滑動關聯
當同時滑動 樓層與基礎信息 並且滑動方向相反就會導致scroll事件的監聽進入死循環導致頁面抖動
為了解決這個問題就設想同時只能滑動一個scroll-view
就寫了三個遮罩層在三個scroll-view上
當 一個scroll-view 觸發了 touchstart 事件其他兩個scroll-view 上面的遮罩層顯示
當 scroll-view 觸發了 touchend 事件三個scroll-view 上面的遮罩層隱藏
這樣就解決了同時觸發兩個@scroll事件進入死循環
<template> <view :style="'height:' + scrollViewHeight + ';'" id="scroll-v3" class="projectProgress"> <view v-if="isFloorMask" class="floor-mask"></view> <view v-if="isTableMask" :style="'height:' + tableHeight + ';'" class="table-mask"></view> <view v-if="isBuildingMask" class="building-mask"></view> <!-- 樓層和項目構件進度匯總 --> <view class="floorsTable"> <scroll-view @touchend="hideMask" @touchstart="floorTouchstart" @scroll="floorScrollHandler" scroll-y="true" :scroll-top="floorScrollTop" :scroll-anchoring="true" style="height: 100%; width: 174rpx"> <view class="floors"> <view class="floorItem" v-for="floor in floorsList" :key="floor">{{ floor }}層</view> </view> </scroll-view> <scroll-view @touchend="hideMask" @touchstart="tableTouchstart" @scroll="tableScrollHandler" scroll-x="true" scroll-y="true" :scroll-top="tableScrollTop" :scroll-left="tableScrollLeft" :scroll-anchoring="true" style="height: 100%; width: calc(100vw - 174rpx)"> <view class="allComponents"> <view class="componentTable"> <componentTable v-for="(buildingItem, index) in boardsInfo" :key="index" :boardsInfo="buildingItem" :floorsList="floorsList"> </componentTable> </view> </view> </scroll-view> </view> <!-- 樓棟和構件類型 --> <view class="projectInfo"> <view class="title"> 項目詳情 </view> <scroll-view @touchend="hideMask" @touchstart="buildTouchstart" @scroll="buildingScrollHandler" scroll-x="true" :scroll-left="buildingScrollLeft" :scroll-anchoring="true" style="width: calc(100vw - 170rpx); height: 196rpx"> <view class="buildingAndType"> <BuildingAndType v-for="(buildingItem, index) in boardsInfo" :key="index" :building="buildingItem.buildingName" :typeList="buildingItem.typeList"> </BuildingAndType> </view> </scroll-view> </view> </view> </template> <script> import debounce from "@/utils/deBounce.js"; import BuildingAndType from "./BuildingAndType.vue"; import componentTable from "./componentTable.vue"; import { getBillBoardsList } from "@/api/project.js"; export default { name: "ProjectProgress", components: { BuildingAndType, componentTable, }, props: { projectId: { type: Number, }, }, created() { this.getBillBoardsList(); }, data() { return { tableHeight:'calc(100vh -20px - 44px - 44px - 196rpx)', isFloorMask: false, isBuildingMask: false, isTableMask: false, scrollViewHeight: "80vh", floorScrollTop: 0, tableScrollTop: 0, tableScrollLeft: 0, buildingScrollLeft: 0, floorScrollFunc: false, tableScrollFunc: false, buildingScrollFunc: false, floorTimer: null, tableTimer: null, buildingTimer: null, boardsInfo: [], floorsList: [], }; }, mounted() { uni.getSystemInfo({ success: (resu) => { this.tableHeight = `calc(100vh - ${resu.statusBarHeight}px - 44px - 44px - 196rpx)` const query = uni.createSelectorQuery(); query.select("#scroll-v3").boundingClientRect(); query.exec((res) => { this.scrollViewHeight = `calc(${resu.windowHeight}px - ${resu.statusBarHeight}px - 44px - 44px)`; }); }, fail: (res) => {}, }); }, methods: { hideMask() { this.isFloorMask = false this.isBuildingMask = false this.isTableMask = false }, initScrollTop(){ this.boardsInfo=[] this.floorsList=[] this.floorScrollTop=0 this.tableScrollTop=0 this.tableScrollLeft=0 }, // 獲取項目下看板的信息 getBillBoardsList() { this.initScrollTop() uni.showLoading({ title:"加載中" }) getBillBoardsList(this.projectId).then((responseData) => { this.boardsInfo = responseData?.data?.data?.buildingUnitVOList; uni.hideLoading() if (this.boardsInfo) { this.floorsListHandler(); } }).catch(()=>{ uni.hideLoading() }) }, // 樓層數據匯總處理 floorsListHandler() { this.floorsList = []; const floorsList = []; this.boardsInfo.forEach((boardInfo) => { Object.keys(boardInfo.floorMap).forEach((floor) => { if (floorsList.indexOf(floor) === -1) { floorsList.push(floor); } }); }); setTimeout(() => { this.floorScrollTop = 68 * this.floorsList.length + Math.random(); this.tableScrollTop = this.floorScrollTop; }, 100); this.floorsList = floorsList; }, floorTouchstart() { this.floorScrollFunc = true; this.isBuildingMask = true this.isTableMask = true }, tableTouchstart() { this.tableScrollFunc = true; this.isFloorMask = true this.isBuildingMask = true }, buildTouchstart() { this.buildingScrollFunc = true; this.isFloorMask = true this.isTableMask = true }, floorScrollHandler(e) { if (this.floorTimer) clearTimeout(this.floorTimer); if (!this.floorScrollFunc) return; this.floorTimer = setTimeout(() => { this.floorScrollFunc = false; }, 200); const { scrollTop } = e.detail; this.tableScrollTop = scrollTop; }, tableScrollHandler(e) { if (this.tableTimer) clearTimeout(this.tableTimer); if (!this.tableScrollFunc) return; this.tableTimer = setTimeout(() => { this.tableScrollFunc = false; }, 200); const { scrollTop, scrollLeft } = e.detail; this.floorScrollTop = scrollTop; this.buildingScrollLeft = scrollLeft; }, buildingScrollHandler(e) { if (this.buildingTimer) clearTimeout(this.buildingTimer); if (!this.buildingScrollFunc) return; this.buildingTimer = setTimeout(() => { this.buildingScrollFunc = false; }, 200); const { scrollLeft } = e.detail; this.tableScrollLeft = scrollLeft; }, }, }; </script> <style lang="scss" scoped> .projectProgress { height: 100%; width: 100vw; background-color: #ffffff; border-top: 2rpx solid #dfdfdf; box-sizing: border-box; .floorsTable { height: calc(100% - 196rpx); background-color: #ffffff; display: flex; box-sizing: border-box; .floors { width: 174rpx; min-height: 100%; color: #333333; font-size: 28rpx; display: flex; flex-direction: column-reverse; .floorItem { box-sizing: border-box; height: 68rpx; width: 100%; text-align: center; border-top: 2rpx solid #dfdfdf; border-right: 2rpx solid #dfdfdf; border-left: 2rpx solid #dfdfdf; padding-top: 10rpx; } } .allComponents { display: flex; flex-direction: row; min-width: calc(100vw - 174rpx); min-height: 100%; flex-direction: column-reverse; .componentTable { display: flex; } } } .projectInfo { height: 196rpx; border: 2rpx solid #dfdfdf; display: flex; box-sizing: border-box; .title { height: 196rpx; line-height: 170rpx; background-color: #f8f8f8; color: #333333; font-size: 28rpx; writing-mode: vertical-lr; text-align: center; z-index: 97; letter-spacing: 8rpx; border-right: 2rpx solid #dfdfdf; box-sizing: border-box; } .buildingAndType { display: flex; } } } .floor-mask { position: fixed; width: 174rpx; height: 100vh; left: 0; bottom: 0; top: 220rpx; background-color: transparent; z-index: 999999; } .building-mask { position: fixed; width: 100vw; height: 196rpx; left: 0; right: 0; bottom: 0; background-color: transparent; z-index: 999999; } .table-mask { position: fixed; width: calc(100vw-174rpx); height: calc(100vh-206px); left: 174rpx; right: 0; bottom: 196rpx; background-color:transparent; z-index: 999999; } </style>