手把手教你如何用Vue實現拖動右側導航選擇聯絡人--V-UI之QuickIndex
先上圖
是不是有種濃濃的原生聯絡人應用的趕腳,下面我們來看看如何實現
》先看看如何使用
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(使用瀏覽器時請調整到手機模式下體驗)
掃碼預覽(推薦)