仿iPhone通讯录制作小程序自定义选择组件
前言
近期閑來無事,想著閑著也是閑著,不如給自己搞點事情做!敢想敢做,于是選擇了給微信小程序做個仿iPhone通訊錄效果的自定義組件。
先來整理一下,瞧瞧需要實現的核心功能。
從易到難,先來看看頁面的結構布局。
基本上分為3塊:
- 頂部的搜索區域;
- 內容的展示區域;
- 側邊字母導航欄區域;
【頂部的搜索區域】
一目了然就直接貼代碼了。 <view class="header">// 這里或許有人要問,為啥不用小程序的label組件呢。?_?// 原因就是...我就不用,你還能咬我?!^(oo)^// 哈哈哈哈~開個玩笑,其實是小程序的label組件還沒支持input!<view class="label"> <icon></icon><input type="text" placeholder="搜索" /></view> </view> 復制代碼【內容的展示區域】
再說一目了然會不會被打呢??
根據圖片就可以看出來,存在2個區域。
代碼如下:
<scroll-view class="scroll"><view class="dl"><view class="dt">這里是字母標題。</view><view class="dd"><span>這里當然是展示的內容啦。</span></view></view></scroll-view> 復制代碼【側邊字母導航欄區域】
為了節省一下文章的篇幅,這里就不貼圖了,很簡單,就是并排下來就好了。
<view class="sub_nav"><view class="option">這里是輸出字母。</view> </view> 復制代碼接下來是wxss的樣式了。
考慮到wxss的樣式較多,我就直接貼代碼鏈接吧,有興趣的童鞋可以瞧瞧。
完成之后,是時候貼個效果圖了。(不許吐槽丑,寶寶會不開心的!?)
結構樣式弄完了,也貼一下自定組件的基礎文件
// index.json {"component": true } 復制代碼// index.js Component({properties: {}, // 組件的對外屬性data: {}, // 組件的內部數據lifetimes: {}, // 生命周期methods: {} // 事件 }); 復制代碼現在開始實現功能了!!!
按照第一個字的首字母排序
說實話,實現這塊功能呢,我是沒啥頭緒的,所以這個時候就要求助偉大的“度娘/Google”了。
經過樓主“遍尋網絡”,查找到如下頁面的源碼參考:
源碼的原理大概描述下:
收錄20902個漢字和375個多音字的Unicode編碼,然后用JS切割首字母并轉換成Unicode進行對比,最后返回對應首字母的拼音。
// 漢字對應的Unicode編碼文件 // oMultiDiff = 多音字 | firstLetterMap = 漢字 import firstStore from './firstChineseLetter'; // 獲取首字母拼音 function getFirstLetter (val) {const firstVal = val.charAt(0);if (/.*[\u4e00-\u9fa5]+.*/.test(firstVal)) {// 處理中文字符// 轉換成Unicode編碼,與firstStore里面的數據進行對比,然后返回對應的參數const code = firstVal.charCodeAt(0); // 轉換成Unicode編碼return code in firstStore.oMultiDiff ? firstStore.oMultiDiff[code] : firstStore.firstLetterMap.charAt(code - 19968);} else {// 這里處理非中文// 檢測是否字母,如果是就直接返回大寫的字母// 不是的話,返回“#”return /^[a-zA-Z]+$/.test(firstVal) ? firstVal.toUpperCase() : '#';} }getFirstLetter('東城區'); // 輸出結果:D復制代碼firstChineseLetter.js地址
獲取首字母的方法有了之后,就該對數據進行處理了。
首先定義一下組件所需要的參數。
Component({// 組件的對外屬性properties: {data: { type: Array, value: [], }, // 組件外傳遞進來的數據attr: { type: String, value: 'label' }, // 需要進行首字母處理的屬性,默認是"label"},... }) 復制代碼然后,針對組件外傳遞進來的數據,做一次轉換。
// 靜態數據的存儲 const Static = {list: [] }Component({...methods: {// 初始/重置數據init () {const { data, attr } = this.properties;let changeData = [], // 轉換后的數據inChangeData = {}; // 存儲轉換后的數據對應字母的索引值data.map(v => {// 獲取首字母拼音let firstLetter = this.getFirstLetter(v[attr]); // 循環對比檢測firstLetter.split('').map(str => {if (str in inChangeData) {// 有首字母相同的項,// 則添加入已有的項里面changeData[inChangeData[str]].list.push(v);} else {// 沒有首字母相同的項,// 則在尾部追加一條新的數據,// 儲存對應的字母值(firstLetter),// 同時存儲該字母對應的索引changeData.push({ firstLetter: str, list: [v] });inChangeData[str] = changeData.length - 1;}});});// 此時轉換后的數組屬于亂序,// 需要對亂序的數組進行排序changeData.sort((pre, next) => pre.firstLetter < next.firstLetter ? -1 : 1);// 若存在“#”項,將位置位移至底部if (changeData[0].firstLetter === '#') {const firstArr = changeData.splice(0, 1);changeData = [...changeData, ...firstArr];}// 存儲轉換后的數據,// this.data.list的數據對應頁面的展示數據,因為有搜索功能,數據可能會變更,// 在靜態的數據里面,也存儲1份數據,方便后續的搜索等功能。this.setData({ list: changeData });Static.list = changeData;},}... }); 復制代碼初始化函數有了之后呢,當然是調用它啦。
Component({lifetimes: {// 在組件實例進入頁面節點樹時執行初始化數據attached () {this.init();}},observers: {// 考慮到組件傳遞的數據存在變更的可能,// 在數據變更的時候,也要做一次初始化'data, attr, icon' (data, attr) {this.init();}}, }) 復制代碼接下來是搜索功能啦~
先給頁面搜索框加個監聽事件(input)
<view class="main">...<view class="header"><view class="label"><icon></icon><input type="text" placeholder="搜索" value="{{ search }}" bindinput="searchData" /></view></view>... </view> 復制代碼接著是JS的事件
const Static = {list: [] }Component({...methods: {searchData (e) {const { value } = e.detail; // 用戶輸入的值const { list } = Static; // init存儲的靜態數據,用來做數據對比const { attr } = this.properties; // 要對比的屬性值let result = [], tem = {};// 沒有搜索內容,返回全部內容if (value.length === 0) { this.setData({ list: Static.list }); return; }// 檢索搜索內容list.map(v => {// 獲取所有跟value匹配上的數據const searchList = v.list.filter(v => v[attr].indexOf(value) !== -1);if (searchList.length > 0) {// 此處原理類似樓上init的對比,此處不細說,// 反正我懶我有理(0.0)if (v.firstLetter in tem) {const _list = result[tem[v.firstLetter]].lish;result[tem[v.firstLetter]].lish = [..._list, ...searchList];} else {result.push({ firstLetter: v.firstLetter, list: [...searchList] });tem[v.firstLetter] = result.length - 1;}}});// 存儲數據this.setData({ list: result, search: value });}},... }); 復制代碼側邊欄字母導航
(突然覺得,寫文好累啊!!!)
寫這塊的時候呢,樓主發現了iPhone通訊錄側邊導航欄有個問題,手指在字母導航欄上滑動的時候,有時候很難確認自己滑到了哪個區域?!
然鵝這個問題呢,樓主發現了微信的通訊錄,針對這塊添加了手指滑動的時候,添加了個結構來幫助用戶確認目前所處的區域。
樓主本著學習的精神,借(chao)鑒(xi)了這個效果,來個效果圖。
貼一下新的wxml結構
<!-- 側邊導航 --><view class="sub_nav" id="subNav" catchtouchstart="subTouchStart" catchtouchmove="subTouchMove" catchtouchend="subTouchEnd"><view class="option" wx:for="{{ list }}" data-firstLetter="{{ item.firstLetter }}" wx:key="firstLetter">{{ item.firstLetter }}<!-- 以下這塊就是新增的結構啦 S --><view class="max {{ item.firstLetter === scrollIntoView && subNavHint ? 'show' : '' }}" data-desc="{{ item.firstLetter }}"></view><!-- 以上這塊就是新增的結構啦 E --></view></view> 復制代碼const Static = {list: [],timer: null }Component({...data: {scrollIntoView: '', // 標記當前處于哪個字母subNavHint: false, // 控制借(chao)鑒(xi)微信效果的元素},methods: {subTouchStart () {this.setData({ subNavHint: true, scrollIntoView: '' });},subTouchEnd () {this.setData({ subNavHint: false });},subTouchMove (e) {// 獲取字母導航欄元素對應的值const query = this.createSelectorQuery();query.select('#subNav').boundingClientRect();query.selectViewport().scrollOffset();query.exec(res => {const { clientY } = e.touches[0]; // Y軸的位置const DomTop = res[0].top; // 導航元素距離頂部的位置const { list } = this.data;// 計算索引,// 或許看到這里有人會疑問,為什么是除以20?// 因為樣式里面,我寫的高度是20px,所以每個字母的區域是20px。let index = Math.round((clientY - DomTop) / 20); index = index >= list.length ? list.length - 1 : index; // 限制索引大于0index = index < 0 ? 0 : index; // 限制索引小于0// 限制結果重復賦值if (list[index].firstLetter !== this.data.scrollIntoView) {this.setData({ scrollIntoView: list[index].firstLetter });// 加個抖動效果wx.vibrateShort(); }});}},}... }); 復制代碼結語
文章寫到這呢,基本上核心的功能都已經實現啦~ ?(終于寫完了...)
通過自己封裝組件,樓主還是有挺大收獲的!
當然,這個組件還有很多可以繼續完善的地方,有興趣的童鞋呢,可以提出你的優化建議,樓主有時(xing)間(qu)的話,會繼續完善下去。
最后,還是推一下這個組件啦,希望它能幫到有需要的童鞋。
github地址
手寫不易,歡迎提issues,歡迎star,內附有使用方法哦。
總結
以上是生活随笔為你收集整理的仿iPhone通讯录制作小程序自定义选择组件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 调用MYSQL存储过程实例
- 下一篇: 计算机组成原理第三版第四章,计算机组成原