自从学习了react后一直琢磨写点项目什么之类的来练练手,恰好公司现在的项目还不是很大,足够我用react进行重构。最开始的时候是想自己去开发组件,后来发现有更好的antd这个东东就放弃了自己开发组件直接将antd的组件拿过来用,感觉挺好用的,直到最近对时间选择器TimePicker这个组件进行实践的时候,发现这个组件的api特别难用(貌似还有bug,应该是我的错觉。。。),所以决定自己开发一个类似的时间选择器来锻炼自己。
言归正传,下面讲解一下我自定义的时间选择器TimePicker开发过程中遇到的坑和目前最终的实现思路(嗯,本人很菜,不吝啬指点...)。
首先构建静态的时间结构,我这里只支持小时和分钟。结构分为上下两层,上层输入框支持直接输入时间,下层选择栏用于给用户提供点击选择,关键代码实现如下:
<div className="my-TimePicker-wrapper">
<div className="my-TimePicker-header">
<span class="ant-time-picker ">
<input className="ant-time-picker-input" />
</span>
</div>
<div className="my-TimePicker-content">
<div className="my-TimePicker-content-box">
<ul className="my-TimePicker-content-menu" >
{
// 你的小时循环遍历
}
</ul>
</div>
<div className="my-TimePicker-content-box">
<ul className="my-TimePicker-content-menu">
{
// 你的分钟循环遍历
}
</ul>
</div>
</div>
</div>
很简单的一段静态结构,css方面抄了antd的一点,自己写了一点,此处不在过多的去描述,各位童鞋可以自己去写适合自己的。嗯,静态结构搭建完毕后下面就是关键的三个部分的描述,也就是输入框的匹配,点击小时和分钟后的事件描述。
第一块:首先来讲解一下小时和分钟的实现的思路和步骤:
当用户点击小时或分钟时,优先需要判断用户点击值是否合法的有效的,比如:你将这个小时和分钟禁用掉了,这时候用户点击肯定是无效的;
其次就是需要判断用户在点击时是否已经点击过其他的小时或着分钟的元素,如果存在点击过的元素,我们需要将上一次存放点击的元素的变量的选中效果进行清除,然后将此次的点击设为选中,并且将此次点击的元素对象和元素值使用变量进行存放;
然后我们玩一个简易的点击选中的元素速度动画(个人兴趣,你也可以不用动画)将选中的元素置顶,
顺手判断一下小时和分钟是否同时存在点击,存在则设置输入框的值,不推荐使用state设置值,存放的变量也不推荐使用state(结束时解释);最后由于我向用户提供了一个onchange方法,所以需要判断一下用户是否设置了onchange,若设置则执行用户的onchange。
关键代码实现如下:
// 以小时为例:
hourClick = (e) => {
let target = e.target, obj = this,
selectedHour = target.innerHTML,
selectedMinute = obj.selectedMinute;
if(target.className === "my-TimePicker-time-selected-disabled"){
// 判断用户点击值是否合法,此处我时判断是否为禁用状态
return ;
}
if(obj.prevHourSelected){
// 判断用户是否已经点击过其他元素,有则将选中清空
obj.prevHourSelected.className = "";
}
// 将此次点击设为选中
target.className = "你的选中类名";
// 将此次点击设置为上一次选中
obj.prevHourSelected = target;
obj.selectedHour = selectedHour;
obj.animateTop(selectedHour, selectedMinute, obj.time); // 简易的点击速度动画
if(selectedMinute !== ""){
// 判断用户的是否同时点击过分钟,若存在则设置输入框的值
}
if(obj.props.onChange){
// 用户的回调函数执行
}
}
第二块,输入框输入时间时的实现思路和步骤:
去除用户输入的值的前后空格,然后判断用户值的长度是否为0,如果输入为0则用户执行了删除操作,此时我们将用户之前输入的值设置的对应的存放变量和选中效果统统清空,然后执行用户回调(如果有的话);
对用户值的长度进行判断是否等于5,因为我是HH:mm的格式,所以长度为5,为什么要进行长度验证,因为input的onchange方法当用户改变值时都会执行方法,我做了正则判断,不通过就会清空,所以需要在长度等于5时才进行正则验证;
通过长度验证后,进行正则验证,不通过就清空值,通过后开始进行设置小时和分钟的选中,同时清掉上一次选中的效果和存放的变量值,接着判断输入的值是否合法,比如你不能输入已经禁用的值,通过后将此次输入的值设置为选中,并存放到变量中,又是判断一下是否有回调;
关键代码实现如下:
if(value.length !== 0){
if(value.trim().length === 5){ // 符合长度要求后进行正则验证
if(obj.reg.test(value)){
let hour = +value.split(":")[0], minute = +value.split(":")[1];
// 获取输入值所在的选项的样式
...
if(obj.prevHourSelected){ // 如果上一次存在选中的时间就清空选中样式
xxx.className = "";
}
if(obj.prevMinuteSelected){ // 如果上一次存在选中的时间就清空选中样式
xxx.className = "";
}
// 如果所选值不是禁止选中,就添加选中样式,并且启用滚动效果
if(hourClass.indexOf("my-TimePicker-time-selected-disabled") === -1 && minuteClass.indexOf("my-TimePicker-time-selected-disabled") === -1){
xxx.className = hourClass + " my-TimePicker-time-selected";
xxx.className = minuteC