先上图
是不是有种浓浓的原生联系人应用的赶脚,下面我们来看看如何实现
》先看看如何使用
let data = ["河北省石家庄市", "河北省唐山市", "山西省太原市", "内蒙包头市",
"辽宁省大连市", "辽宁省鞍山市", "辽宁省抚顺市", "吉林省吉林市",
"黑龙江省齐齐哈尔市", "江苏省徐州市", "浙江省杭州市", "福建省福州市",
"江西省南昌市", "山东省济南市", "山东省青岛市", "山东省淄博市",
"河南省郑州市", "$我是随意加的","!哈哈哈","@哇哇哇","湖南省长沙市", "贵州省贵阳市", "云南省昆明市",
"甘肃省兰州市", "新疆乌鲁木齐市"]
this.$refs.quickIndexHook.init(data);
从代码中我们可以看出,数据为一个数组。然后将数组以参数的形式调用QuickIndex的init方法,在QuickIndex中重要数据有两部分,一部分是右侧A-Z为固定部分,另一部分为列表数据,列表数据为传入的数据和相应的拼音首字母组成。这里有个疑问,这里传入数组我们如何获取数组中元素对应的拼音首字母呢?这里推荐看一篇我前期的文章javascript实现根据汉字获取拼音或者获取拼音首字母,因为QuickIndex是在获取到拼音的背景下开发的。
》进入正题
布局部分
通过布局可以看到我们的页面为分为三部分,列表数据部分、触控区A-Z、当前触摸位置提示部分。我们通过监听触控区的触摸事件,从而实现列表部分相应滚动。下面我们分别看看初始化部分和对应的touch事件
init
// 根据key 比较大小进行排序
compare(propertyName) {
return function (object1, object2) {
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (value2 < value1) {
return 1;
} else if (value2 > value1) {
return -1;
} else {
return 0;
}
}
},
// 初始化
init(data) {
this.index = -1;
// 一、处理数据
let tempData = data || [];
// 排序
tempData = tempData.sort();
let preCharacter = "";
let key = "";
let temp = [];
//二、生成key和是否显示key
for (let i = 0; i < tempData.length; i++) {
// 调用pinyin.js里面的方法获取首个汉字拼音首字母
key = getPinYinFirstCharacter(tempData[i], "", true).substring(0, 1)
temp.push({
key: key,
value: tempData[i],
showKey: preCharacter != key
})
preCharacter = key;
}
// 三、按key排序
temp = temp.sort(this.compare("key"))
// 四、key还有重复 去掉重复key
for (let i = temp.length - 1; i >= 1; i--) {
if (temp[i].key === temp[i - 1].key) {
temp[i].showKey = false;
} else {
temp[i].showKey = true;
}
}
this.quickIndexDatas = temp;
this.refresh();
}
init代码逻辑:由于传入的数据可能是无序的数据,所以我们需要将传入的数据进行一次排序,排序完成之后我们就可以得到相对有序的数据了,然后通过遍历获取文字拼音首字母,拼接成一个对象放入到一个临时数组中。key为A-Z,value为对应的文字,由于存在同一个key多个value,所以我们需要控制显示同一个key值显示一次,所以在对象中添加了一个showKey用于区分。通过这一步我们可以得到一个临时数组,其中元素为{key:"A",value:"阿拉斯加",showKey:true/false}这样的形式,由于不同汉字可能开头的首字母是一样的,所以我们需要针对key进行排序(compare函数)。到了这一步之后数组排序的问题已经完成,仍然有一个待解决的问题是showKey可能存在相同的key,showKey两个或者多个都为true的情况,也就是说列表会显示两个或多个key。这里我们就需要对key进行排查去掉重复的showKey=true的情况,只让列表数据拥有相同key的对象中只有第一个对象的showKey为true,其他统统为false。
整过初始化过程主要分为四步:
一、传入的数组进行首次排序。
二、遍历获取首字母并组成一个对象放入临时数组
三、根据key排序
四、去掉重复显示的key
处理触摸部分
touchstart:
touchstart(e) {
this.showIndicator = true;
this.getMoveY(e);
}
touchstart代码逻辑相对比较简单,触摸开始的时候显示A-Z提示,并设置提示所在的位置
touchmove:
touchmove(e) {
this.getMoveY(e);
// 计算当前位置
this.index = this.currentIndex(moveY);
}
touchmove部分一是获取当当前触摸位置进行相应的换算得到moveY值,设置A-Z提示位置,然后调用currentIndex传入移动的moveY,计算出当前移动到A-Z的中某个元素的索引。
获取moveY并设置提示位置和获取索引
getMoveY(e) {
moveY = e.touches[0].clientY - this.top;
moveY = moveY <= this.width / 2 ? this.width / 2 : moveY;
moveY = moveY >= this.height - this.width / 2 ? this.height - this.width / 2 : moveY;
this.indicator.style.top = this.top + moveY - 28 + 'px';
}
currentIndex(scrollY) {
let height1;
let height2;
for (let i = 0; i < this.keys.length; i++) {
height1 = i * this.width;
height2 = (i + 1) * this.width;
if (i === this.keys.length - 1 || (scrollY >= height1 && scrollY <= height2)) {
return i;
}
}
return 0;
}
touchend:
touchend() {
this.showIndicator = false;
this.index = this.currentIndex(moveY)
moveY = 0;
}
触摸结束之后,隐藏A-Z提示,获取当前位置对应的索引值(这一步用户点击滚动到相应位置),将moveY重置为0。
监听索引值的变化列表数据进行相应的滚动
watch: {
// 根据索引值判断是否滚动
index(val) {
if (this.keys[val] === "☆") {
let tip = this.$refs.quickIndexHook.getElementsByClassName("quick_index_item_tips")[0]
if (tip) {
this.scroll.scrollToElement(tip)
}
return;
}
for (let i = 0; i < this.keyLocations.length; i++) {
if (this.keys[val] === this.keyLocations[i].key) {
let tip = this.$refs.quickIndexHook.getElementsByClassName("quick_index_item_tips")[i]
if (tip) {
this.scroll.scrollToElement(tip)
}
break;
}
}
}
}
以上部分即为QuickIndex的核心代码,部分较为简单就不一一解释了,若有不明白的可在评论区提出。
注:滚动部分使用了处理效果比较好的better-scroll库进行滚动
最后个人通过vue开发了一套前端h5 ui(v-ui),实现了44个类似原生效果的控件,皆以vue组件形式提供,有兴趣的可以看一看,说不一定就有你需要的。
项目github地址v-ui
预览地址:v-ui(使用浏览器时请调整到手机模式下体验)