需求
我们在开发过程中有时候会遇到左侧导航菜单栏数据需要监听和右侧顶部导航菜单联动效果。这里我们通常使用锚点定位和滚动监听方法实现。这里我们使用两种方案解决,第一是常规的出来方法,第二是通过uniapp里面的scroll-view进行处理
具体实现方案如下
一、vue里面处理,通过监听滚动事件和定义元素
1- 代码如下
<template><div><div class="outBox"><div class="first"><div :class="['level', {'select': i == first.select}]" style="background-color: #666;" v-for="(v, i) in first.list">{{ v }}</div></div><div class="second" id="select"><div :class="['level', {'select': i == second.select}]" style="background-color: #666;" v-for="(v, i) in second.list">{{ v }}</div></div><div class="content" id="content"><div v-if="status" class="box box1" style="height: 100px;">二级类目1 商品内容</div><div v-if="status" class="box box2" style="height: 344px;">二级类目2 商品内容</div><div v-if="status" class="box box3" style="height: 470px;">二级类目3 商品内容</div><div v-if="status" class="box box4" style="height: 230px;">二级类目4 商品内容</div></div></div></div>
</template>
<script>
export default {data() {return {first: {select: 0,list: ['一级类目1', '一级类目2', '一级类目3', '一级类目4', '一级类目5']},second: {select: 0,list: ['二级类目1', '二级类目2', '二级类目3', '二级类目4']},list: [],timer: null,status: true}},methods: {scroll() {let scrollTop = document.getElementById('content').scrollToplet i = this.list.findIndex(v => v >= scrollTop)this.second.select = iif (scrollTop >= this.list[this.list.length - 1] - 20) {this.timer && clearTimeout(this.timer)this.timer = setTimeout(() => {this.second.select = 0this.first.select += 1document.getElementById('content').removeEventListener('scroll', this.scroll)document.getElementById('content').scrollTo(0, 10)document.getElementById('content').addEventListener('scroll', this.scroll)// 判断如果左侧导航菜单数据没有的情况默认选中定义导航菜单if (this.first.select >= this.first.list.length) {this.first.select = 0}}, 300)} else if (scrollTop == 0) {this.timer && clearTimeout(this.timer)this.timer = setTimeout(() => {this.second.select = 0if (this.second.select > 0) {this.first.select -= 1} else {// 滚动到顶部刷新数据this.first.select = 0}document.getElementById('content').removeEventListener('scroll', this.scroll)document.getElementById('content').scrollTo(0, 10)document.getElementById('content').addEventListener('scroll', this.scroll)}, 300)}}},mounted() {let o = document.getElementsByClassName('box')for ( let i = 0; i < o.length; i++) {// 监测右侧导航菜单距离顶部的距离60this.list[i] = i > 0 ? o[i-1].scrollHeight + this.list[i - 1] + (60 * (i > 1 ? 1 : 0)) : 10}let scrollHeight = document.getElementById('content').scrollHeightlet offsetHeight = document.getElementById('content').offsetHeightif (scrollHeight - offsetHeight < this.list[this.list.length - 1]) {this.list[this.list.length - 1] = scrollHeight - offsetHeight}document.getElementById('content').addEventListener('scroll', this.scroll)}
}
</script>
<style>
.outBox{width: 400px;height: 300px;margin: 0;padding: 0;background-color: #eee;position: relative;}.first{position: absolute;left: 0;top: 0;width: 100px;height: 100%;overflow: auto;background-color: rgb(102, 102, 102);}.first .level{height: 30px;font-size: 14px;text-align: center;line-height: 30px;}.second{position: absolute;left: 100px;top: 0;width: calc(100% - 100px);height: 60px;overflow: auto;background-color: #06c;display: flex;align-items: center;justify-content: space-around;}.second .level{height: 30px;font-size: 14px;text-align: center;line-height: 30px;}.content{position: absolute;left: 100px;top: 60px;width: calc(100% - 100px);height: calc(100% - 60px);min-height: 240px;overflow: auto;box-sizing: border-box;}.box{background-color: aquamarine;margin-bottom: 20px;}.select{color: #f56c6c;}
</style>
2- 实现结果如下:
二、uni-app使用scroll-view锚点定位和滚动监听功能
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>
js
<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', //点击选项的idpicked:0, // 左侧选中选项的indexnowRightIndex:0, // 右边当前滚动的indexitemArr:[], //用于存放右侧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的indexif(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>
css
.sortBox{height: 100%;width: 100%;box-sizing: border-box;/* padding: 12px; */padding-bottom: 0;position: relative;}.boxTop{width: 100%;position: fixed;top: 44px;left: 0;height: 220px;}.sort-search{height: 50px;display: flex;justify-content: space-between;padding: 12px;padding-bottom: 0;box-sizing: border-box;}.sort-search-left{box-sizing: border-box;width: 100px;height: 38px;line-height: 38px;border-radius: 16px;border: 1px solid #f0f0f0;text-align: right;padding: 0 10px;background: url('../../static/ping.png') left center no-repeat;background-size: 45%;}.sort-search-right{width: 36px;height: 100%;margin-right: 80px;border: #f0f0f0 1px solid;border-radius: 19px;text-align: center;line-height: 36px;}.sort-collect{margin-top: 15px;}.sort-broadcast{width: 100%;height: 30px;background-color: #fff7fa;display: flex;font-size: 18px;line-height: 30px;padding: 0 10px;box-sizing: border-box;font-weight: bold;}.sort-type{display: flex;height: 60px;box-sizing: border-box;align-items: flex-end;justify-content: space-evenly;font-size: 16px;font-weight: bold;color: #909090;}.sort-type-item{display: flex;align-items: center;height: 40px;}.sort-type-item-img{height: 30px;margin-right: 6px;}.sort-type-item-text{height: 100%;line-height: 40px;}.sort-type-item-text-checked{border-bottom: #ff9092 3px solid;color: #41414d;}.opcity{opacity: 0;}.sortBox-scoll{position: absolute;top: 220px;left: 0;width: 100%;background-color: #f0f0f0;height: calc(100vh - 314px);overflow-y: auto;}
2- 实现结果如下: