一、功能点描述
点击选择右侧字母时左边城市列表自动滑动到对应的字母区域
二、设计方式
兄弟组件间的数据传递,因为层级结构简单,所以采用的是子组件传递数据给父组件,然后父组件再传递数据给子组件
1、子组件(字母表)传递给父组件(城市)
<template>
<ul class="list">
<li
class="item"
v-for="item of letters"
:key="item"
//绑定click事件执行handleLetterClick函数
@click="handleLetterClick"
>
{{item}}
</li>
</ul>
</template>
// 其他就不写 就写这个函数
methods: {
//事件触发后会传递一个event对象。其中包含了点击标签的内容
handleLetterClick (e) {
//发送'change'信号给父组件。带上了标签文本内容
this.$emit('change', e.target.innerText)
}
}
2、父组件(城市)传递给另外一个子组件(各城市列表)
<template>
<div>
<city-header></city-header>
<city-search></city-search>
<city-list
:cities = "cities"
:hot = "hotCities"
//3. 然后再传递给子组件
:letter = "letter"
></city-list>
<city-alphabet
:cities="cities"
//1. 其他不管就这个。监听change事件,调用handleLetterChange函数
@change="handleLetterChange"
></city-alphabet>
</div>
</template>
data () {
return {
cities: {},
hotCities: [],
letter: ''
}
}
//2. 将子组件的数据复制给letter变量
handleLetterChange (letter) {
this.letter = letter
}
3、子组件(城市列表)接收传递来的数据进行跳转
props: {
hot: Array,
cities: Object,
// 1. 就这货接收数据
letter: String
},
mount
//2. 然后使用监听函数watch 监听letter的变化。一旦发生变化就滚动到对应元素的位置
watch: {
letter () {
if (this.letter) {
//4. 根据letter的值跳转到对应ref的标签对象。
const element = this.$refs[this.letter][0]
//5. 利用scrollToElement方法跳转到对应的元素即跳转完成
this.scroll.scrollToElement(element)
}
}
}
<div class="area"
v-for="(item, key) of cities"
:key="key"
//3. 这里将标签对象与key值做绑定
:ref="key"
>
三、手指滑动列表随动(以上功能为点动)
监听手指的变化
<template>
<ul class="list">
<li
class="item"
// 6.这里改成letters 而不是cities了(由于通过计算函数计算了城市列表)
v-for="item of letters"
:key="item"
:ref="item"
//1. 手指开始触碰字母列表
@touchstart="handleTouchStart"
//2. 字母开始移动字母列表
@touchmove="handleTouchMove"
//3.字母离开屏幕
@touchend="handleTouchEnd"
//这个是前面的那个监听事件不管他
@click="handleLetterClick"
>
{{item}}
</li>
</ul>
</template>
//4. 使用计算函数 给字母表生成一个数组。等下再定位字母的时候会用到
//同时 template页面也要跟着改
computed: {
letters () {
const letters = []
for (let i in this.cities) {
letters.push(i)
}
return letters
}
},
// 监听的函数具体实现:
data () {
return {
//用来判断拇指是否接触了字母列表
touchStatus: false
}
}
handleTouchStart () {
this.touchStatus = true
},
handleTouchMove (e) {
if (this.touchStatus) {
// 7. 先获取到A字母的标签与Header.vue组件下沿的距离
const startY = this.$refs['A'][0].offsetTop
// 8. 然后获取到拇指接触屏幕的高度减去Header.vue组件的高度 获得Header.vue组件下沿与拇指的高度 79为header.vue的高度
const touchY = e.touches[0].clientY - 79
// 9. 处理这两个元素的高度距离之后除以每个字母标签的高度(20)并去除小数点。 获得字母列表的所在顺序
const index = Math.floor((touchY - startY) / 20)
// 10. 判断字母是不是已经跑出字母表之外了
if (index >= 0 && index < this.letters.length) {
// 11. 将字母列表的所在顺序当做letters的下标 触发兄弟组件传递。完成
this.$emit('change', this.letters[index])
}
}
},
handleTouchEnd () {
this.touchStatus = false
}