微信小程序 - 按钮文本框 “giao-text”

最近因为体验了微信小程序 “百万工具箱” 里面的正则表达式工具,感觉到手机端输入正则表达式中的特殊字符十分的繁琐(反人类)。思前想后,决定撸一个可以解决繁琐输入的文本框组件。

不多说,先上开源地址吧:
npm https://www.npmjs.com/package/giao-text
github https://github.com/oloshe/giao-text

初定开发需求:

普通字符使用自带输入法输入,正则表达式常用字符(如:\ | $ ^ )利用组件提供快速输入。

初定的实现思路:
  1. 声明属性 stringSet,通过传入的字符串,分割成若干个字符,生成字符串数组 stringArray;
  2. 利用 stringArray 循环渲染出若干按钮,形成一个按钮面板;
  3. 为按钮面板的按钮添加 tap 事件,点击按钮输入框添加对应字符。


    初始效果

此时遇到的难点

  1. 点击按钮时,焦点会发生变化。当焦点在输入框上时,手机会显示输入键盘,当焦点不在输入框时,输入键盘自动会收回。点击按钮输入字符后,想要继续键盘输入需要再点击输入框,十分的不友好。
  2. 点击按钮添加文本需要在光标 cursor 之后添加,而不是字符串末。

解决思路

  1. 点击按钮添加文本之后,自动把焦点转移到输入框。
  2. 监听输入框失去焦点(blur)事件,因为该事件会返回光标位置,所以必须等到该事件之后才能添加文本。

流程图

流程图

理清大概流程之后,先从左侧(focus in input)入手。

  1. onBlur 输入框失去焦点事件返回获取光标位置(cursor);
  2. onTap 按钮点击事件返回获取对应文本(text);
  3. 传入两个参数到方法 setText 给输入框文本赋值。

此时遇到的难点(坑)

在这种情况下 setText 必须等两个函数都调用完毕再调用,因为 setText的两个参数要分别从 blur 和 tap 事件获取。那么确认一下顺序,通过赋值的方法把第一个参数存起来,到第二个事件调用的时候再执行 setText 函数不就行了?

给两个事件加上打印函数,运行看看打印结果。

onBlur(event) {
 console.log('Blur');
}
onTap(event) {
  console.log('Tap');
}

打印结果

Blur
Tap

如果这时你按照这个结果去设计,你就掉坑了!手机调试时输出的结果刚好时相反的!所以必须不管哪个函数先调用,都要等到两个函数调用完毕再执行 setText。所以在某一个函数加 if 判断是不可行的。但是在两边都加 if 判断又显得十分地不优雅,那怎么办呢?

解决方案

  1. 在 Blur 函数调用 setData 把 cursor 保存;
  2. 在 Tap 函数调用 setData 把 text 保存;
  3. 分别添加 cursor 和 text 的数据监听,当两个字段修改时,调用对应函数;
  4. 添加字段 _tapEvent 、 _blurEvent 记录两个事件的调用情况,取值 0 或 1;
  5. 添加 _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
... ... ... ...

你可能感兴趣的:(微信小程序 - 按钮文本框 “giao-text”)