前言

近期闲来无事,想着闲着也是闲着,不如给自己搞点事情做!敢想敢做,于是选择了给微信小程序做个仿iPhone通讯录效果的自定义组件。

先来整理一下,瞧瞧需要实现的核心功能。

  1. 按照第一个字的首字母排序;
  2. 实现输入搜索功能;
  3. 侧边栏字母导航;

从易到难,先来看看页面的结构布局。

基本上分为3块:

  • 顶部的搜索区域;
  • 内容的展示区域;
  • 侧边字母导航栏区域;
// index.wxml
<view class="main"><!-- 顶部搜索区域 --><view class="header"></view><!-- 内容区域 --><scroll-view class="scroll"></scroll-view><!-- 侧边导航 --><view class="sub_nav"></view>
</view>
复制代码

【顶部的搜索区域】

一目了然就直接贴代码了。

<view class="header">// 这里或许有人要问,为啥不用小程序的label组件呢。?_?// 原因就是...我就不用,你还能咬我?!^(oo)^// 哈哈哈哈~开个玩笑,其实是小程序的label组件还没支持input!<view class="label"> <icon></icon><input type="text" placeholder="搜索" /></view>
</view>
复制代码

【内容的展示区域】

再说一目了然会不会被打呢??
根据图片就可以看出来,存在2个区域。

  1. 红框包围的外框,负责圈定展示的范围;
  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通讯录制作小程序自定义选择组件相关推荐

  1. 微信小程序自定义标签组件component封装、组件生命周期,组件通信

    微信小程序自定义标签组件component封装.组件生命周期,组件通信 本文来说下小程序的自定义标签组件封装. 相比于vue,react的非路由组件,微信小程序的component组件要麻烦些,而且生 ...

  2. 小程序自定义键盘组件之车牌号录入

    小程序自定义键盘组件之车牌号录入 效果图: html: <view bindtap="showAddCarNumFun">点击新增车牌号</view>< ...

  3. 微信小程序自定义音频组件,自定义滚动条,单曲循环,循环播放

    小程序自定义音频组件,带滚动条 摘要:首先自定义音频组件,是因为产品有这样的需求,需要如下样式的 而微信小程序API给我们提供的就是这样的 而且产品需要小程序有后台播放功能,所以我们不考虑小程序的 a ...

  4. 微信小程序自定义日历组件

    微信小程序自定义日历组件 wxml <view class="maskWrap" bindtap="close"></view>< ...

  5. 微信小程序地址选择组件(仿拼多多App版地址选择)

    先上效果图 这是一个地址选择组件    点击选择地址  启动组件选择完成  返回组件中选择的地址 首先先写组件代码   getAddress.js var city = require('../../ ...

  6. 微信小程序自定义弹窗组件 action-sheet

    最近公司在开发短剧小程序,短剧的剧集展示交互需要用到组件 action-sheet,小程序自带的有这个组件,但是这个组件支持的功能比较单一,相当于是一个选择表一样,不能自定义很多内容,只能自定义一个组 ...

  7. java switch小程序,小程序自定义switch组件

    如上图,小程序api中的switch组件只能自定义颜色,不能自定义宽高,所以就开始了自己写switch组件. 自定义组件样式 switch组件样式大致如图,样式思路:未选中时为一个长方形有圆角按钮,和 ...

  8. 微信小程序自定义波浪组件

    最近看到好多app上有波浪背景,有动态的,有静态的,这里是在小程序中用得动态. 先看看效果图:里面的文本是组件内部定义的. 这是用两个svg的图片用css关键帧动画做的效果(这里谢谢子弹短信里前端群的 ...

  9. uniapp | 小程序自定义头部组件

    前言:在开发小程序的过程中,很经常会遇到各种各样的需求,有些顶部没法很单一的只显示标题和纯色的背景,因此就需要按照项目需求自定义. 思路:先根据设计稿在页面中绘制出结构和样式的内容,接着固定的样式值改 ...

  10. 微信小程序-自定义导航组件

    一.如何自定义组件 从小程序基础库版本 1.6.3 开始,小程序支持简洁的组件化编程.所有自定义组件相关特性都需要基础库版本 1.6.3 或更高. 开发者可以将页面内的功能模块抽象成自定义组件,以便在 ...

最新文章

  1. 安装linux和windows双系统
  2. 计算机控制面板没有笔和触摸,如何通过注册表或者 命令 或者服务 关闭 控制面板笔和触摸里面的笔势操作(控制面板-笔和触摸-笔势) !急!...
  3. python路径拼接问题
  4. MyBatis框架:延迟加载策策略、一级缓存、二级缓存
  5. 【NOIP】关押罪犯
  6. cvi中c语言只保留两位小数,CVI编程常见问题与错误-2012.9
  7. 前端学习(1271):async/await处理多个异步请求
  8. Linux下增加swap分区
  9. 网域高科家具行业网站开发管理系统
  10. 茅粉又疯狂!“蒂芙尼蓝”茅台炒至8500!
  11. 加密保护软件 WinLicense常见问题整理大全(四)
  12. 2006 年100 款最佳安全工具谱
  13. Apache Flink 进阶(六):Flink 作业执行深度解析
  14. 前复权 后复权和不复权是什么意思
  15. myFAX传真服务器
  16. 产品说接口返回数据需要脱敏 只能安排
  17. Date类型接收空字符串(@InitBinder注解实现)
  18. Android开发必备工具
  19. 漫画 | 如何让产品经理不改需求?
  20. Java中文乱码浅析及解决方案

热门文章

  1. 字节跳动sql笔试题
  2. 阿里云mysql数据库日志_阿里云mysql数据库操作日志
  3. 2021Java春招,java求职简历模板下载
  4. 分享一下我自己做的新媒体运营月报,有人看吗?
  5. Oracle备份恢复之闪回技术
  6. 【纯干货】中国的支付清算体系是怎么玩的?
  7. 也谈POV旋转LED制作,经验及技术点。
  8. 大漠插件7.2209
  9. 使用for循环打印出大写字母的ASCII码对照表(c语言实现)
  10. 好看的php表格样式,HTML5制作表格样式