我们先来看一下整体布局效果:
city
首先,我们想要实现城市左右布局可以滚动需要下载一个插件 betterScroll
然后引入插件,如下所示:
npm i better-scroll
import BScroll from 'better-scroll'
initScroll () {
this.$nextTick(() => {
if (!this.Scroll) {
this.Scroll = new BScroll(this.$refs.body, {
click: true, // 配置允许点击事件
scrollY: true, // 可以开启纵向滚动
probeType: 3, // 开启滚动监听
bounce: false // 关闭弹性效果
})
//Scroll这个对象监听事件,实时获取位置pos.y
this.Scroll.on('scroll', (pos) => {
// console.log(pos);
this.scrollY = Math.abs(Math.round(pos.y))
})
} else {
// 重新计算 better-scroll,当 页面大小发生变化的时候务必要调用确保滚动的效果正常
this.Scroll.refresh()
}
})
},
重点:
getCity () {
var that=this
axios.get('/city.json').then(function (res){
that.cityList=res.data.data.cities
that.$nextTick(()=>{
that.initScroll()
that.calculateHeight()
})
})
},
这里的代码一定要在获取数据里面写nextTick()回调里面写代码,因为需要等待数据加载再去初始化scroll和获取右边每一个盒子的高度。
下面我们说一说高度如何获取
calculateHeight () {
let flagList = this.$refs.body.getElementsByClassName('flag')
console.log(flagList);
//每个盒子的高度a-z
let height = 0
this.listHeight.push(height)
// console.log(this.listHeight);
for (let i = 0; i < flagList.length; i++) {
let item = flagList[i]
//获取城市首字母到页面顶部的高度
height += item.clientHeight
this.listHeight.push(height)
}
},
这里就是获取到每一个盒子的clientHeight的高度进行叠加,在push到一个数组里面
给大家看一下完整的代码吧
HTML:
<template>
<div class="wrapper">
<!-- 头部搜索区域 -->
<cityHeader></cityHeader>
<!-- 城市列表 -->
<div class="body" ref="body">
<!-- 左侧城市数据 -->
<div class="wrap">
<div class="city-list flag" v-for="(items, key) in cityList" :key="key">
<div class="tit">{{key}}</div>
<div class="btm-wrap">
<div class="item" v-for="item in items" :key="item.id">{{item.name}}</div>
</div>
</div>
</div>
</div>
<!-- 右侧城市索引值 -->
<div class="asside">
<div class="item" :class="{'current': currentIndex === index}" v-for="(item, index) in Letter"
:key="item" @click="selectMenu(index)">{{item}}</div>
</div>
</div>
</template>
这里的头部我是分装了一个组件,你们有需要的话可以根据自己的需求去编写
Script:
<script>
import BScroll from 'better-scroll'
import cityHeader from '@/components/cityHeader.vue'
import axios from 'axios'
export default {
data () {
return {
cityList: [],
listHeight: [],
scrollY: 0,
}
},
mounted () {
this.getCity()
},
computed: {
//获取城市的首字母
Letter(){
let arr=[]
for(let key in this.cityList){
arr.push(key)
}
return arr
},
currentIndex () {
for (let i = 0; i < this.listHeight.length; i++) {
//点击索引值 每个盒子的总高度
let height1 = this.listHeight[i]
let height2 = this.listHeight[i + 1]
// console.log(height1,height2); 0 947
//点击的字母所在的高度到浏览器顶部的距离如果为空或者盒子顶部到浏览器顶部的距离>=上一个盒子的高度和盒子顶部到浏览器顶部的距离<点击的字母所在的高度到浏览器顶部的距离
if (!height2 || (this.scrollY >= height1 && this.scrollY < height2)) {
// console.log(i); 下标
return i
}
}
return 0
}
},
methods: {
getCity () {
var that=this
axios.get('/city.json').then(function (res){
that.cityList=res.data.data.cities
that.$nextTick(()=>{
that.initScroll()
that.calculateHeight()
})
})
},
initScroll () {
this.$nextTick(() => {
if (!this.Scroll) {
this.Scroll = new BScroll(this.$refs.body, {
click: true, // 配置允许点击事件
scrollY: true, // 可以开启纵向滚动
probeType: 3, // 开启滚动监听
bounce: false // 关闭弹性效果
})
//Scroll这个对象监听事件,实时获取位置pos.y
this.Scroll.on('scroll', (pos) => {
// console.log(pos);
this.scrollY = Math.abs(Math.round(pos.y))
})
} else {
// 重新计算 better-scroll,当 页面大小发生变化的时候务必要调用确保滚动的效果正常
this.Scroll.refresh()
}
})
},
calculateHeight () {
let flagList = this.$refs.body.getElementsByClassName('flag')
console.log(flagList);
//每个盒子的高度a-z
let height = 0
this.listHeight.push(height)
// console.log(this.listHeight);
for (let i = 0; i < flagList.length; i++) {
let item = flagList[i]
//获取城市首字母到页面顶部的高度
height += item.clientHeight
this.listHeight.push(height)
}
},
selectMenu (index) {
let flagList = this.$refs.body.getElementsByClassName('flag')
// //每个盒子的下标
let el = flagList[index]
// console.log(el);
// //滚动事件
//最后一步是如何实现点击的时候去让右边的滚动到指定的位置。
this.Scroll.scrollToElement(el, 300)
}
},
components: {
cityHeader
},
}
</script>
CSS:
<style scoped>
.wrapper {
width: 100%;
}
.wrapper .header {
width: 100%;
height: 50px;
background: #ffffff;
padding: 0 10px 0 12px;
box-sizing: border-box;
display: flex;
align-items: center;
}
.wrapper .header .icon-arrow {
font-size: 24px;
color: #242424;
margin-right: 8px;
}
.wrapper .header .search {
width: 90%;
height: 34px;
background: #f9f9f9;
border-radius: 30px;
overflow: hidden;
position: relative;
}
.wrapper .header .search .search-inp {
width: 100%;
height: 34px;
background: #f9f9f9;
padding: 0 30px 0 30px;
box-sizing: border-box;
}
.wrapper .header .search .icon-search {
font-size: 18px;
position: absolute;
top: 50%;
left: 10px;
transform: translateY(-50%);
}
.wrapper .header .search .icon-voice {
font-size: 18px;
position: absolute;
top: 50%;
right: 10px;
transform: translateY(-50%);
}
.wrapper .body {
width: 100%;
position: absolute;
top: 50px;
bottom: 0;
overflow: hidden;
}
.wrapper .body .wrap {
width: 100%;
}
.wrapper .body .wrap .hot {
border-bottom: 1px solid #e0e0e0;
}
.wrapper .body .wrap .local .tit,
.wrapper .body .wrap .history .tit,
.wrapper .body .wrap .hot .tit{
color: #9b9b9b;
padding: 10px 0 10px 15px;
box-sizing: border-box;
}
.wrapper .body .wrap .list {
width: 100%;
display: flex;
flex-wrap: wrap;
margin-left: 15px;
}
.wrapper .body .wrap .list .item {
width: 100px;
height: 32px;
border: 1px solid #c9c9c9;
box-sizing: border-box;
background: #fff;
text-align: center;
line-height: 32px;
margin-right: 10px;
margin-bottom: 10px;
color: #232323;
}
.wrapper .body .wrap .local .list .item {
margin-bottom: 0;
}
.wrapper .body .wrap .local .list .item .icon-local {
font-size: 13px;
color: #FFA500;
margin-right: 2px;
}
.wrapper .body .wrap .history .list .item:nth-last-child(-n+2) {
margin-bottom: 0;
}
.wrapper .body .wrap .city-list .tit{
color: #9b9b9b;
font-size: 12px;
padding: 5px 0 5px 15px;
box-sizing: border-box;
border-bottom: 1px solid #e0e0e0;
}
.wrapper .body .wrap .city-list .btm-wrap {
padding-left: 15px;
background: #fff;
}
.wrapper .body .wrap .city-list .btm-wrap .item{
width: 100%;
text-align: left;
color: #2f2f2f;
padding: 15px 0;
border-bottom: 1px solid #e0e0e0;
}
.wrapper .asside {
text-align: center;
position: fixed;
top: 60px;
right: 10px;
}
.wrapper .asside .item {
font-size: 12px;
color: #636363;
padding: 6px 0;
box-sizing: border-box;
}
.wrapper .asside .item.current {
color: #FFA500;
}
</style>