vue列表左右联动效果
沉默的小猴子 人气:0先谈需求:左侧为分类列表,点击分类名右侧商品列表会滑动对应分类到顶部;右侧商品列表滑动到某一分类时左侧分类列表对应分类会滑动到顶部高亮显示。
再来说说思路:
1、引用swiper插件,这种方法冲突和不适配可能性太多,会越改越麻烦所以弃用。
2、使用页面滑动scroll事件,引入better-scroll组件。这个较为轻便,可自定义性较大,可以使用。
实现步骤:
1、左右两侧均使用v-for循环列表,使用index来索引目录。JS中使用数组下标来和列表index对应,实现左右互通功能。
2、左右列表都在循环列表的父层增加ref绑定,获取对应的列表滚动,以及对应元素高度,从而根据高度来判断需要滑动的距离。
效果图:
页面基础代码:
<template> <div class="wrap"> <!-- 顶部搜索 --> <div class="topMenu"> <img class="back_l" @click="goBack" src="./img/backicon@2x.png"> <div class="section"> <img class="saleR" src="./img/sub.png"> <input placeholder="搜索商家和商品" auto-focus @keyup.enter="submitTop"/> </div> <img class="saleR" @click="goMap" src="./img/citydw.png"> </div> <div class='no_sroll' v-if="navList"> <aside class="tabNav" ref="l_list"> <ul> <li ref="l_item" class="nav_li" :class="(TabNavList == index) ? 'checkIn' : ''" :index="index" :id="index" @click="checkNavList(item, index)" v-for="(item, index) in navList" :key="index"> <img class="imgLi" src="./img/shu@2x.png" v-if="TabNavList == index" />{{item.gcName}} </li> </ul> </aside> <!-- 增加浮动层 --> <div class="theFixed" :class="(TabNavList == index) ? 'isFixed' : 'isHide'" v-if="scrollTrue"> <div class="leftName">{{scrollTrue.gcName}}</div> <div class="rightBtn" @click="goTwoClass(scrollTrue)">全部分类<img class="r_img_btn" src="./img/jiantou.png"></div> </div> <section class="newHeight" ref="r_list"> <div > <div class="proList" v-for="(item, index) in navList" :key="index" ref="good"> <div class="r_top"> <div class="leftName">{{item.gcName}}</div> <div class="rightBtn" @click="goTwoClass(item)">全部分类<img class="r_img_btn" src="./img/jiantou.png"></div> </div> <div class="r_cont"> <div class="cu-items" v-for="(item, index) in (item.childList)" :key="index" @click="goThreeCalss(item)"> <div class="storeL"> <img v-if="item.classImg" :src="item.classImg" alt=""/> <img v-else src="../../carManage/img/zwt.png"/> </div> <div class="text">{{item.gcName}}</div> </div> </div> </div> </div> </section> </div> </div> </template> <script> // import {baseUrl} from '@/common/js/paths' import Better from 'better-scroll' export default { name: 'allStoreClass', components: { Better }, data () { return { loadStatus: true, bodyHeight: window.innerHeight + 'px', storeName: '', distance: '', positionArea: '', positionPoint: { lng: '', lat: '' }, center: { lng: '', lat: '' }, zoom: 15, index: 0, gcName: '', gcParentId: '', searchBarFixed: false, scrollY: 0, // 定义的Y滚动轴及初始值 TabNavList: 0, // 左右联动取值 scrollTrue: '', // 右侧吸顶 navList: [], // 全局列表 isScroll: false, pageIndex: 1, pageSize: 20, markerPoint: { 'lng': '', 'lat': '' }, arr: [0], flag: true, obj: null, show: false, ios: /iphone os/g.test(window.navigator.userAgent.toLowerCase()) } }, computed: { // 001 }, created () { // this.$store.commit('setTopDisplay', false) // this.$store.commit('setPageTitle', '分类列表') if (this.$store.state.platform === 'app') { // eslint-disable-next-line no-undef let location = JSON.parse(native.getLocation()) this.positionPoint.lng = location.longitude this.positionPoint.lat = location.latitude this.positionArea = location.city this.getList() } else if (this.$store.state.from === 'bjsh') { // 更改北京石化来源 this.getList() } else { this.getPositionJs() this.getList() } }, mounted () { this.$nextTick(() => { this._initScroll() this._getHeight() }) if (this.$store.state.platform === 'app') { // eslint-disable-next-line no-undef native.ifShowTitleBarView(false) } else if (this.$store.state.platform === 'web') { this.$store.commit('setTopDisplay', false) } }, methods: { goBack () { if (this.$store.state.platform === 'app') { // eslint-disable-next-line no-undef native.goBack() } else if (this.$store.state.from === 'hnsh') { // 更改与北京石化交互 // 河南石化交互 let params = { type: 'turnback', index: -1 } this.$bridge.callhandler('phonebridge', params, (data) => { }) } else { history.go(-1) } }, _initScroll () { this.left = new Better(this.$refs.l_list, { click: true, probeType: 3 }) this.rgt = new Better(this.$refs.r_list, { probeType: 3, click: true }) this.rgt.on('scroll', (res) => { if (this.flag) { this.scrollY = Math.abs(res.y) + 16 // 页面内有一个16像素的顶部状态栏 for (let i = 0; i < this.arr.length; i++) { if (this.scrollY > this.arr[i] && this.scrollY < this.arr[i + 1]) { this.TabNavList = i - 1 // 左右联动取值 // console.log(this.navList[this.TabNavList].gcName) // 取出元素的gcName this.scrollTrue = this.navList[this.TabNavList] this.isScroll = true // document.getElementById(this.TabNavList).scrollIntoView() this.left.scrollToElement(this.$refs.l_list, 100, 0, this.TabNavList * 60) } } } }) this.left.on('scroll', (res) => { if (this.flag) { this.scrollY = Math.abs(res.y) + 16 this.left.scrollToElement(this.$refs.l_list[this.TabNavList], 100, 0, 0) } }) }, _getHeight () { // // 开始改造 let rightItems = this.$refs.r_list.getElementsByClassName('proList') setTimeout(() => { // 根据betterScroll定义滚动 console.log(rightItems) // 右侧列表数组内容 console.log(rightItems.length) // 右侧列表数组长度 if (rightItems && (rightItems.length > 0)) { let height = 0 this.arr.push(height) for (let i = 0; i < rightItems.length; i++) { let item = rightItems[i] height += item.clientHeight this.arr.push(height) } } }, 600) }, getList (e) { this.axios.get(`v4/goodsManage/goodsClass`, { params: { keywords: e || '', // 关键词 pageNo: this.pageIndex, pageSize: this.pageSize } }).then((res) => { if (res.data.isSuccess) { // console.log(res.data.resultData) this.navList = res.data.resultData // this.scrollTrue = res.data.resultData[0] } else { this.$toast(res.data.message) } }).catch(err => { console.log(err) this.$toast('请求失败,请稍后重试') }) }, getPositionJs () { let _this = this // eslint-disable-next-line no-undef var geolocation = new BMap.Geolocation() geolocation.getCurrentPosition(function (r) { // eslint-disable-next-line no-undef if (this.getStatus() === BMAP_STATUS_SUCCESS) { console.log('是否成功定位') _this.positionPoint.lng = r.point.lng _this.positionPoint.lat = r.point.lat _this.positionArea = r.address.city } else { var info = '未能获取当前地理定位, 请检查手机是否已打开位置服务! ' + '\n' + '失败: ' + this.getStatus() alert(info) } }, {enableHighAccuracy: true}) }, getDetails (it) { this.show = !this.show this.obj = it }, // 顶部搜索 submitTop (e) { console.log(e.target.value) let obj = e.target.value this.getList(obj) }, // 顶部跳地图 goMap () { this.$router.push({name: 'nearStore', query: {from: 'storeIndex', sortFlag: this.curSort, gcName: this.gcName, gcParentId: this.gcParentId, lat1: this.positionPoint.lat, lon1: this.positionPoint.lng}}) }, // 左侧选择TAB checkNavList (e, v) { console.log(e, v) this.gcName = e.gcName this.gcParentId = e.gcParentId this.flag = false this.TabNavList = v // 左右联动取值 this.rgt.scrollToElement(this.$refs.good[v], 100, 0, 0) setTimeout(() => { this.flag = true }, 100) // document.getElementById(v).scrollIntoView() // this.searchBarFixed = true }, // 一级分类查看全部 goTwoClass (e) { console.log(e) this.$router.push({name: 'classProList', query: {gcName: e.gcName, gcParentId: e.gcId, gcId: ''}}) }, // 去二级分类详情 goThreeCalss (e) { console.log(e) this.$router.push({name: 'classProList', query: {gcName: e.gcName, gcParentId: '', gcId: e.gcId}}) }, mapClick (item) { // item.storeName, item.distance, item.storeWd, item.storeJd let lng = item.storeJd let lat = item.storeWd let name = item.storeName if (this.$store.state.platform === 'app') { if (typeof (native) !== 'undefined') { // eslint-disable-next-line no-undef native.daoHangWithLat(lat, lng, name) } } else if (this.$store.state.from === 'hnsh') { let params = { startlat: this.positionPoint.lat, startlong: this.positionPoint.lng, endlat: lat, endlong: lng, type: 'nav' } this.$bridge.callhandler('phonebridge', params, (data) => { }) } else { location.href = 'https://api.map.baidu.com/marker?location=' + lat + ',' + lng + '&title=目的位置&content=' + name + '&output=html&hidjhnavigation=1&jhWebView=1' } }, mapModalClick (lng, lat, name, distance, address) { this.mapModal = true this.$router.push({name: 'bdMap', query: {lng: lng, lat: lat, name: name, distance: distance, address: address}}) /* this.center.lng = lng this.center.lat = lat this.markerPoint.lng = lng this.markerPoint.lat = lat this.storeName = name */ /* var opts = { width: 380, height: 180, opacity: 0.5, // eslint-disable-next-line no-undef offset: new BMap.Size(0, -30), title: '<p style="font-size: 20px;color: #2ca90e;margin:0;margin-bottom: 20px;">这里是内容</p>', enableMessage: false } // eslint-disable-next-line no-undef var map = new BMap.Map('canvasMap') // eslint-disable-next-line no-undef var infoWindow = new BMap.InfoWindow('', opts) // eslint-disable-next-line no-undef map.openInfoWindow(infoWindow, new BMap.Point('0', '0')) */ // eslint-disable-next-line no-undef map.centerAndZoom(new BMap.Point(lng, lat), 18) }, syncCenterAndZoom (e) { const {lng, lat} = e.target.getCenter() this.center.lng = lng this.center.lat = lat this.zoom = e.target.getZoom() }, draw ({el, BMap, map}) { const pixel = map.pointToOverlayPixel(new BMap.Point(116.404, 39.915)) el.style.left = pixel.x - 60 + 'px' el.style.top = pixel.y - 20 + 'px' }, destroyed () { window.removeEventListener('scroll', this.handleScroll, true) } } } </script> <style scoped> .wrap{ margin: 0 auto; width: 100%;height:100%;overflow:hidden;} .topMenu{ width: 100%; position: absolute; margin: 0 auto; padding:7.5px 15px; background:#fff; z-index:19; display: flex; justify-content: space-between; } .section { width:80%; height:30px; background:rgba(242,242,242,1); border-radius:15px; display: flex; margin-left:15px; margin-right:15px; } .section input{ font-size:12px; font-weight:400; color:rgba(153,153,153,1); line-height:20px; border: none; background:rgba(242,242,242,1); outline:none; } .back_l{ width:30px; height:30px; } .saleR{ width:30px; height:30px; } .no_sroll {margin:0 auto; width:100%;height:100%;padding-top:45px; overflow:hidden; position:relative; display:flex;} .main { display: -webkit-flex; display: flex; flex-flow: column nowrap;justify-content: center;align-items: center; height: 100%;text-align: center;margin-bottom: 20px;} .top_top { margin-top:-50px; position:relative; z-index:999;} .marginTop{margin-top:45px;} .goback {position: absolute; left: 0; top: 0; width: .9rem; padding-left: .3rem; text-align: left;} .newHeight {display:block; overflow: hidden; background:#fff; position:relative; width:75%;padding:0 10px;overflow-y:scroll;overflow-x:hidden;-webkit-overflow-scrolling: touch;} .newHeight .cu-items { position: relative; display: flex; float:left; flex-direction:column; align-items: center; width:30%; height:110px; margin-left: 4.5%; margin-bottom:10px;} .newHeight .cu-items:nth-of-type(3n-2) { margin-left:0;} .cu-items .storeL { width: 80px; height: 80px; position: relative; border-radius: 4px; } .cu-items .storeL img { width: 80px; height: 80px;} .cu-items .text {font-size:14px; line-height:18px; margin-top:5px; color:#666;} .newHeight .proList { width:100%; display:flex; flex-direction:column; position:relative;} .newHeight .proList .r_top { height:60px;display:flex;justify-content:space-between;align-items:center;} .newHeight .proList .r_top .leftName { line-height:60px; font-size:16px; text-align:left; color:#666; margin-left:10px;} .newHeight .proList .r_top .rightBtn {font-size:12px;line-height:60px;color:#2CBF64;} .newHeight .proList .r_top .rightBtn .r_img_btn {width:11px;height:11px;margin-left:5px;transform:rotate(270deg);} .theFixed {height:60px;display:flex;justify-content:space-between;align-items:center;} .theFixed .rightBtn {font-size:12px;line-height:60px;color:#2CBF64;} .theFixed .rightBtn .r_img_btn {width:11px;height:11px;margin-left:5px;transform:rotate(270deg);} .theFixed .leftName {line-height:60px; font-size:16px; text-align:left; color:#666; margin-left:10px;} .tabNav { display: block;width:25%; background:#F5F5F2;overflow: hidden; position:relative; overflow-y:scroll; overflow-x:hidden; -webkit-overflow-scrolling: touch;} .tabNav::-webkit-scrollbar { display:none;} .tabNav::scrollbar { display:nonel} .tabNav .nav_list { display:flex; width:100%;} .tabNav .nav_li { font-size:16px; line-height:20px; color:#666; text-align:center; height: 60px; width:100%; flex-shrink: 0; position:relative; display: flex; justify-content: center; align-items: center;} .tabNav .nav_li .imgLi { height:16px; position: absolute; left:0; top: 22px;} .checkIn { color:#2CBF64!important; background:#fff!important; font-weight:bold;} .isFixed { height:60px; line-height:60px; font-size:16px; text-align:left; color:#666; position:fixed; width:70%; left:27%; background: #fff; z-index: 19; } .isHide { position: fixed; height:60px; line-height:60px; font-size:16px; text-align:left; color:#666; width:70%; left:27%; background: #fff; z-index: 19; } </style> <style> .BMap_cpyCtrl { display: none; } .anchorBL { display: none; } .BMap_noprint.anchorBL { display: block; bottom: 259px!important; } .BMap_stdMpCtrl { display: block; bottom: 269px!important; } .BMap_pop { position: absolute;} </style>
备注:代码做了二期优化
加载全部内容