小程序实现按字母排列选择城市

小程序实现按字母排列选择城市

实现效果预览

在这里插入图片描述

实现思想

  1. 利用小程序腾讯地图将所有城市查出来,并将其渲染至页面(https://lbs.qq.com/qqmap_wx_jssdk/index.html)(其中字母栏也根据获取到的数据变化)
  2. 其中涉及三个交互(点击字母时滚动到相应位置;滑动触摸字母时,需滚动到相应位置,并有当前哪个字母的提示,且有震动感;手动滑动页面时,需将当前对应的字母选中)
  3. 滑动触摸字母时,首先要得到所有字母所在块的高度,再平均的获取到每个字母的高度。当触摸滚动时,拿到pageY(距离文档左上角的距离,具体解释官网有https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html#touches)
  4. 计算出所有字母内容的高度,并存为一个数组
  5. 利用pageY计算出可能到达的字母位置的下标(pageY-字母栏的top值/每个字母的高度)
  6. 将计算出的下标所对应的字母内容高度赋值给scroll-top值
  7. 手动滚动列表时,根据滚动的距离计算出当前滚动的下标值,将字母数组的对应的下标值做处理
  8. 需要注意setData不能频繁使用,所以在使用的时候,需要做处理和优化

实现知识点

  1. 字母滚动到相应位置需使用scroll-view组件中的scroll-into-view 设置其子元素的id值
  2. 滑动触摸字母,需使用小程序事件touchmove事件和touchend事件
  3. 手动滑动页面时,需使用scroll-view中的scroll-top属性设置竖向滚动条位置

代码

wxml

      <!--pages/findHome/selectCity/index.wxml-->
<view class="selectCity">
  <view class="searchCity">
    <input placeholder="输入城市名进行搜索" bindinput="getSuggest" bindfocus="inputFocus"></input>
  </view>
  <view class="cityContainer" style="padding-top: {{searchCity}}px">
    <scroll-view scroll-y="true" scroll-into-view="{{toView}}" scroll-top="{{scrollTop}}" bindscroll="scroll" style="height: 100%" scroll-with-animation="ture">
      <block wx:for="{{citys}}" wx:for-index="key" wx:for-item="value" wx:key="key">
        <view class="cityItem">
          <view class="citytype" id="{{value.id}}">{{key}}</view>
            <block wx:for="{{value.data}}" wx:for-key="i" wx:for-item="ele" wx:key="i">
                <view class="cityDetail" data-name="{{ele.fullname}}" bindtap="confrimCity">{{ele.fullname}}</view>
            </block>
        </view>
      </block>    
    </scroll-view>  
  <view class="cityAZ">
    <block wx:for="{{letter}}" wx:key="{{id}}">
      <view bindtap="letterClick" class="AZ {{!touchFlag && activeAZ == item.id ? 'activeAZ' : ''}}" data-id="{{item.id}}" catchtouchmove="whenTouch" catchtouchend="touchEnd" id="{{item.id}}">{{item.name}}
        <view class="AZInfo" wx:if="{{touchFlag && activeAZ == item.id}}">
          {{item.name}}
          <view class="trigle"></view>
        </view>
      </view>
    </block>
  </view>
  </view>
</view>


wxss

/* pages/findHome/selectCity/index.wxss */
.selectCity {
  width: 100vw;
  height: 100vh;
}
.searchCity {
  height: 70rpx;
  line-height: 70rpx;
  width: 100%;
  padding: 0 24rpx;
  position: fixed;
  top: 0;
  left: 0;
  background: #fff;
  z-index: 10;
}
.cityContainer {
  height: 100%;
}
.cityItem {
  padding: 0 70rpx 0 24rpx;
}
.citytype {
  height: 70rpx;
  background: #F5F5F5;
  line-height: 70rpx;
  padding: 0 24rpx;

}
.cityDetail {
  height: 80rpx;
  line-height: 80rpx;
  padding: 0 24rpx;
  border-top: 1px solid #DCDCDC;
  border-bottom: none;
}
.cityDetail:last-child {
  border-bottom: 1px solid #DCDCDC;
}
.cityAZ {
  position: fixed;
  top: 136rpx;
  right: 0;
  font-size: 28rpx;
  padding: 0 24rpx;
  /* background: #fff; */
  width: 40rpx;
  text-align: center;
}
.AZ {
  position: relative;
  border-radius: 50%;
}
.activeAZ {
  background: orange;
  color: #fff;
}
.AZInfo {
  width: 70rpx;
  height: 70rpx;
  border-radius: 50%;
  text-align: center;
  color: #fff;
  line-height: 70rpx;
  background: orange;
  position: absolute;
  left: -94rpx;
  top: -14rpx;
}
.trigle {
  width: 0;
  height: 0;
  border: 32rpx solid orange;
  border-right: none;
  border-top-color: transparent;
  border-bottom-color: transparent; 
  position: absolute;
  top: 4rpx;
  right: -9rpx;
}

js

// pages/findHome/selectCity/index.js
let cityDatas = require('../../../utils/cityData.js');
let QQMapWX = require('../../../libs/qqmap-wx-jssdk.js');
let qqmapsdk = new QQMapWX({
  key: '4WKBZ-ADX36-MGNS4-E6TFJ-Q6JJE-YBF2A'
});
Page({

  /**
   * 页面的初始数据
   */
  data: {
    citys: {},//获取到的所有城市
    letter: [], //获取到的所有字母
    searchCity: 0,
    toView: '', //点击跳转的id
    scrollTop: '',
    citysHeight: [],//所有字母大模块的top
    azHeight: 0,   //每个字母平均的高度
    azTop: 0,
    index: '',
    activeAZ: 'A1',
    touchFlag: false
  },
  letterClick: function (e) {
    this.setData({
      touchFlag: false,
      toView: e.currentTarget.dataset.id
      // activeAZ: e.currentTarget.dataset.id,
    })
  },
  confrimCity() {
    wx.switchTab({
      url: '/pages/findHome/index',
    })                
  },
  whenTouch(e) {
    let index = 0;
    if((e.touches[0].pageY -  this.data.azTop) % this.data.azHeight == 0){
      index = (e.touches[0].pageY - this.data.azTop) / this.data.azHeight
    }else {
      index = parseInt((e.touches[0].pageY - this.data.azTop) / this.data.azHeight);
      if(this.data.index !== index && index < this.data.letter.length) {
        this.data.index = index;
        this.setData({
          scrollTop: this.data.citysHeight[index],
          activeAZ: this.data.letter[index].id,
          touchFlag: true
        })
        wx.vibrateShort();
      }
    }
  },
  touchEnd() {
    setTimeout(()=>{
      this.setData({
        touchFlag: false
      })
    },600)
  },
  scroll(e) {
    let scrollHeight = e.detail.scrollTop;
    let index = this.calculateIndex(this.data.citysHeight, scrollHeight);
    if (this.data.index !== index && index < this.data.letter.length) {
      this.setData({
        index: index,
        activeAZ: this.data.letter[index].id,
        touchFlag: false
      })
    }
  },
  calculateIndex(arr, scrollHeight) {
    let index = 0;
    for (let i = 0; i < arr.length; i++) {
      if (scrollHeight >= arr[i - 1] && scrollHeight < arr[i]) {
        index = i - 1;
        break;
      }else if(scrollHeight >= arr[arr.length-1]) {
        index = arr.length - 1;
        break;
      }else if(0 < scrollHeight < arr[0]) {
        index = 0
      }
    }
    return index;
  },
  getSuggest(e) {
    console.log(e)
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    let query = wx.createSelectorQuery();
    query.select('.searchCity').boundingClientRect(rect => {
      this.setData({
        searchCity: rect.height
      })
    }).exec();
    qqmapsdk.getCityList({
      success: (res) => {//成功后的回调
        res.result[1].forEach(ele => {
          //如果城市对象中已经存在该字母开头的
          if (this.data.citys[ele.pinyin[0].charAt(0).toUpperCase()]){
            this.data.citys[ele.pinyin[0].charAt(0).toUpperCase()].data.push(ele);
          }else {
            this.data.citys[ele.pinyin[0].charAt(0).toUpperCase()] = {id: '',data: []};
            this.data.citys[ele.pinyin[0].charAt(0).toUpperCase()].id = ele.pinyin[0].charAt(0).toUpperCase()+1;
            this.data.citys[ele.pinyin[0].charAt(0).toUpperCase()].data.push(ele);
          }
        })
        let newArr = Object.keys(this.data.citys).sort();
        let sortCity = {};
        newArr.forEach(ele => {
          this.data.letter.push({name: ele, id: ele+1})
          sortCity[ele] = this.data.citys[ele]
        })
        this.setData({
          citys: sortCity,
          letter: this.data.letter,
          citysHeight: []
        });
        //获取个字母大模块的top值
        query.selectAll('.cityItem').boundingClientRect((rect) => {
          this.data.citysHeight = [];
          rect.forEach(ele => {
            this.data.citysHeight.push(ele.top - this.data.searchCity)
          })
        }).exec();

        //获取已有字母的高度
        let winH = wx.getSystemInfoSync().windowHeight;  
        query.select('.cityAZ').boundingClientRect((rect) => {
         this.data.azHeight = rect.height / this.data.letter.length;
         this.data.azTop = rect.top;
        }).exec();
      },
      fail: function (error) {
        console.error(error);
      },
      complete: function (res) {
      
      }
    });
    this.setData({
      toView: 'A1'
    });

  }
})
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值