去哪儿实现地址联动滚动

324 阅读1分钟

2021-07-11

用的better-scroll实现的地址上拉刷新和下拉加载,better-scroll主要的注意事项,最主要的是父元素要固定宽度,可用flex:1来固定,子元素宽度自动撑开就好。

主要的实现效果和思路

address2.png

address.png

点击跳转到具体位置,比如点到右边字母区域A,就会自动跳转到A区域

运用better-scroll的一个api,scrollToElement

props: {
        cityData: Object,
        hotCities: Array,
        targetEl: String // 传过来的点击时候的元素
    },
watch: {
        targetEl () {
            if(this.targetEl){
                // 滚动到对应元素
                this.scroll.scrollToElement(this.$refs[this.targetEl][0])
            }
        }
    },
mounted () {  
        let wrapper = this.$refs.listWrapper
        this.scroll = new BScroll(wrapper)
        this.$nextTick(() => {
            this.scroll.refresh()
        })
    }

这里有个要注意的地方,需要DOM更新完毕之后,再刷新一下滚动元素 this.scroll,否则的话,写在mounted里面,滚动的元素是通过后端数据返回的,这时候后端数据还没得到,可能会有滚动不了的情况发生,所以需要再次刷新一下。

在字母区滑动,地址区域也会滑动

主要用到三个移动端手指触发事件,想要计算出是移动到了哪个字母。

  1. touchstart:手指触摸屏幕时候。去获取A字母距离顶端的距离
  2. touchmove:手指在屏幕移动的时候。可以得到手指距离顶部的距离,每一个移动的动作,都会有对应的手指的位置,手指的位置 - A字母距离顶端的距离,从而计算出此刻移动的这个字母到A字母的距离/22(每个字母的高度),就可以计算出移动到哪个字母的下标了。
  3. touchend:手指离开屏幕的时候

主要的实现代码

<template>
  <div class="al-list">
    <ul class="al-box">
      <li
        class="item"
        v-for="item of alArray"
        :key="item"
        :ref="item"
        @click="getAlphbat"
        @touchstart="handleTouchStart"
        @touchmove="handleTouchMove"
        @touchend="handleTouchEnd"
      >
        {{ item }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
    data () {
        return {
            aHeight: '',
            touchFlag: false // 要不要触发移动move事件标识值
        }
    },
    props: {
        cityData: Object
    },
    computed: {
        alArray () {
            const alArray = Object.keys(this.cityData)
            return alArray
        }
    },
    methods: {
        getAlphbat (e) {
            this.$emit('change', e.target.innerText)
            // this.targetEl = e.target.innerText
        },
        handleTouchStart () {
            this.touchFlag = true
            // this.height没有值才会去获取,否则还是用原来的this.height
            // 获取A元素到父元素顶部的距离
            if(!this.aHeight){
                this.aHeight = this.$refs['A'][0].offsetTop
            }
        },
        handleTouchMove (e) {
            if(this.touchFlag){
                // 移动到某个元素的下标
                let moveLetterIndex = Math.floor((e.touches[0].clientY - this.aHeight) / 22)
                this.$emit('change', this.alArray[moveLetterIndex])
            }             
        },
        handleTouchEnd () {
            // 手指一离开字母表区域,就不能触发移动move事件
            this.touchFlag = false
        }  
    }
}
</script>

<style lang='stylus' scoped>
@import '~@assets/mixin.styl';

.al-list {
  height: 100%;
  width: 20px;
  position: absolute;
  top: 0;
  right: 0;

  .al-box {
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center;

    .item {
      text-align: center;
      padding: 5px 0;
      color: $themColor;
    }
  }
}
</style>

总结

几个易混的知识点总结如下:

  1. for in和for of的区别
  2. computed计算属性需要有返回值
  3. 遍历对象,可以用Object.keys(obj):遍历键名;Object.values(obj):遍历键值