最近因为体验了微信小程序 “百万工具箱” 里面的正则表达式工具,感觉到手机端输入正则表达式中的特殊字符十分的繁琐(反人类)。思前想后,决定撸一个可以解决繁琐输入的文本框组件。
不多说,先上开源地址吧:
npm https://www.npmjs.com/package/giao-text
github https://github.com/oloshe/giao-text
初定开发需求:
普通字符使用自带输入法输入,正则表达式常用字符(如:\ | $ ^ )利用组件提供快速输入。
初定的实现思路:
- 声明属性 stringSet,通过传入的字符串,分割成若干个字符,生成字符串数组 stringArray;
- 利用 stringArray 循环渲染出若干按钮,形成一个按钮面板;
-
为按钮面板的按钮添加 tap 事件,点击按钮输入框添加对应字符。
初始效果
此时遇到的难点
- 点击按钮时,焦点会发生变化。当焦点在输入框上时,手机会显示输入键盘,当焦点不在输入框时,输入键盘自动会收回。点击按钮输入字符后,想要继续键盘输入需要再点击输入框,十分的不友好。
- 点击按钮添加文本需要在光标 cursor 之后添加,而不是字符串末。
解决思路
- 点击按钮添加文本之后,自动把焦点转移到输入框。
- 监听输入框失去焦点(blur)事件,因为该事件会返回光标位置,所以必须等到该事件之后才能添加文本。
流程图
流程图理清大概流程之后,先从左侧(focus in input)入手。
- onBlur 输入框失去焦点事件返回获取光标位置(cursor);
- onTap 按钮点击事件返回获取对应文本(text);
- 传入两个参数到方法 setText 给输入框文本赋值。
此时遇到的难点(坑)
在这种情况下 setText 必须等两个函数都调用完毕再调用,因为 setText的两个参数要分别从 blur 和 tap 事件获取。那么确认一下顺序,通过赋值的方法把第一个参数存起来,到第二个事件调用的时候再执行 setText 函数不就行了?
给两个事件加上打印函数,运行看看打印结果。
onBlur(event) { console.log('Blur'); } onTap(event) { console.log('Tap'); }
打印结果
Blur Tap
如果这时你按照这个结果去设计,你就掉坑了!手机调试时输出的结果刚好时相反的!所以必须不管哪个函数先调用,都要等到两个函数调用完毕再执行 setText。所以在某一个函数加 if 判断是不可行的。但是在两边都加 if 判断又显得十分地不优雅,那怎么办呢?
解决方案
概念图
- 在 Blur 函数调用 setData 把 cursor 保存;
- 在 Tap 函数调用 setData 把 text 保存;
- 分别添加 cursor 和 text 的数据监听,当两个字段修改时,调用对应函数;
- 添加字段 _tapEvent 、 _blurEvent 记录两个事件的调用情况,取值 0 或 1;
- 添加 _tapEvent, _blurEvent 数据监听器,当两个字段都为 1 时,调用 setText。
代码(部分)
observers:{
"_cursor": function(_cursor) {
this.setData({ _blurEvent: 1 })
},
"_text": function (_text) {
this.setData({ _tapEvent: 1 })
},
"_blurEvent, _tapEvent": function(blur,tap) {
if (blur === 1 && tap === 1) {
this._setText(this.data._cursor,this.data._text);
}
setTimeout(() => {
this.data._tapEvent = 0;
this.data._blurEvent = 0;
},200);
}
},
methods:{
onBlur(event) {
let { cursor } = event.detail;
this.focused = false;
this.setData({ _cursor: cursor });
this.triggerEvent('blur',cursor);
},
onTap(event) {
const { index } = event.currentTarget.dataset;
let _text = this.data.stringArray[index];
this.setData({ _text: _text });
},
_setText(cursor,text) {
let value = this._insertString(this.data.value,cursor,text);
cursor = this.data._cursor + text.length;
this.setData({
value,
cursor,
focus: true,
});
this.triggerEvent('change',value);
},
_insertString(str,index,insertStr) {
return str.slice(0,index) + insertStr + str.slice(index);
}
}
_blurEvent, _tapEvent 监听器中 setTimeout 的作用是及时清空事件状态,200为该事件时存在时长,作用是让 blur 和 tap 事件的时间误差增大到 200ms,这样不管谁先谁后,两个函数在这 200ms 内都执行了就调用 setText 方法。
ps:如果在这期间某个方法执行多次也不会造成错误,都不会出错,因为数据都存在。
第二种情况
解决了左侧(focus in input)的问题,右侧(focus not in input)的问题就迎刃而解,只需要在 _text 监听器时判断是否有焦点,有焦点就是左侧的问题,如果没有焦点,则直接执行 setText 方法,因为他并不需要等待 blur 函数 或者 _cursor 的值,因为焦点不在输入框上,不会触发 blur 事件,而 cursor 的值直接取即可,cursor 值不会清除。
代码
"_text": function (_text) {
if (this.focused) {
this.setData({
_tapEvent: 1,
})
} else {
this._setText(this.data._cursor, this.data._text);
}
},
小结
至此,初定的需求已经解决了。该组件将会不断完善,一直在往可拓展性、实用性的方向发展。
该组件目前解锁了很多实用有趣的功能!将会在微信小程序“百万工具箱”最新版本的正则表达式中应用,感兴趣的小伙伴欢迎前来一看!
正则表达式组件试用
API(部分)
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
string-set | 字符集 | String | - |
separator | 分隔符,用于分割stringSet | String | '' |
columns | 列数,每行的最大按钮个数 | Number | 6 |
distinct | 是否去重 | Boolean | false |
combo | 字符长度为2时,是否将光标移至中间 | Boolean | false |
clearable | 是否显示清除按钮 | Boolean | false |
random | 按钮是否随机分配位置(在每次输入框获取焦点时) | Boolean | false |
control-keep | 按钮面板是否保持显示 | Boolean | false |
scroll | 是否为滚动显示(单行) | Boolean | false |
... | ... | ... | ... |
网友评论