基于 Input 组件进行拓展的 InputNumber 组件
InputNumer 组件的难点在于:
实现鼠标长按, 计数器数值变动;
导致 InputNumber 组件的值变化, 有以下操作 v-model 绑定值的变化, 加, 减按钮, input 组件的输入, 需要对上述结果进行处理, 所以如何设计合理模式, 减少冗余代码.
1. 实例
代码
- <!-- 基础用法 -->
- <fat-input-number v-model="value" />
- <!-- 禁用状态 -->
- <fat-input-number disabled />
- <!-- 步数 -->
- <fat-input-number :step="5" v-model="stepValue" />
- <!-- 最大, 最小值限制 -->
- <fat-input-number :max="20" :min="0" :step="5" v-model="value" />
实例地址: InputNumber 实例
代码地址: GitHub UI-Library
2. 原理
首先利用 Input 组件, 在其 slot prepend 和 slot append 两个插槽内, 插入相应的加, 减按钮, 具体如下.
- <fat-input
- class="input-number-inner"
- type="text"
- :disabled="disabled"
- v-model="inputValue"
- v-bind="$attrs"
- >
- <template slot="prepend">
- <div
- :class="['prepend-part', {'is-disabled': addDisabled }]"
- @mousedown.stop="!addDisabled && handleClick('add')"
- >
- <fat-icon name="add" size="16"/>
- </div>
- </template>
- <template slot="append">
- <div
- :class="['append-part', {'is-disabled': decDisabled }]"
- @mousedown.stop="!decDisabled && handleClick('dec')"
- >
- <fat-icon name="remove" size="16"/>
- </div>
- </template>
- </fat-input>
接下來实现加, 减按钮的长按功能, 其本质就是将 click 事件分为了, mousedown 以及 mouseup 两个事件来处理, 具体体现在 handleClick 函数.
- handleClick(type) {
- const { step } = this;
- const period = 100;
- const timerHandler = () => {
- const { addDisabled, decDisabled } = this;
- if (!addDisabled && type === "add") this.inputNumberValue += step;
- if (!decDisabled && type === "dec") this.inputNumberValue -= step;
- };
- const timer = setInterval(timerHandler, period);
- const startTime = new Date();
- const handler = () => {
- const endTime = new Date();
- if (endTime - startTime <period) timerHandler();
- clearInterval(timer);
- document.removeEventListener("mouseup", handler, false);
- };
- document.addEventListener("mouseup", handler, false);
- }
首先, 监听按钮的 mousedown , 当它触发时完成以下操作:
记录触发时间 startTime;
定义一个 setInterval, 每隔一个 period 就调用相应的 timerHandler 函数, 来模拟长按操作;
在 document 上监听 mouseup 事件, 当鼠标还原时触发.
然后, 当鼠标还原时, 就会触发 document 的 mouseup 事件, 此时相应的处理为:
记录触发时间为 endTime, 如果满足
endTime - startTime < period
条件, 则执行一次 timerHandler;
利用 clearInterval 停止 setInterval, 之后移除对 mouseup 的监听.
由于每次修改 InputNumber 组件的值时, 都需判断是否超出最大, 最小值, 或是输入的值是否为数字等规则, 即使将这些规则抽象为一个函数, 也会显得代码有些臃肿, 所以选用 computed 属性对 inputValue 状态进行一层代理, 具体如下
- computed: {
- inputNumberValue: {
- get() {
- return this.inputValue;
- },
- set(value) {
- const { min, max, inputValue } = this;
- const limits = [{
- need: value => !isNum(value),
- value: inputValue
- }, {
- need: value => value>= max,
- value: max
- }, {
- need: value => value <= min,
- value: min
- }, {
- need: () => true,
- value: value
- }];
- this.inputValue = limits.find(limit => limit.need(value)).value;
- }
- }
- }
将对 inputValue 的修改, 转变为对 inputNumberValue 的修改, 同时劫持 get,set 函数, 在 set 中, 定义规则.
3. 使用
实现了基础的 InputNumber, 后续可以在自行在样式上进行扩展.
- <!-- 基础用法 -->
- <fat-input-number v-model="value" />
- <!-- 禁用状态 -->
- <fat-input-number disabled />
- <!-- 步数 -->
- <fat-input-number :step="5" v-model="stepValue" />
- <!-- 最大, 最小值限制 -->
- <fat-input-number :max="20" :min="0" :step="5" v-model="value" />
往期文章:
从零实现 vue 的组件库 (零)- 基本结构以及构建工具
从零实现 Vue 的组件库 (一)- Toast 实现
从零实现 Vue 的组件库 (二)- Slider 实现
从零实现 Vue 的组件库 (三)- Tabs 实现
从零实现 Vue 的组件库 (四)- File-Reader 实现
从零实现 Vue 的组件库 (五)- Breadcrumb 实现
从零实现 Vue 的组件库 (六)- Hover-Tip 实现
从零实现 Vue 的组件库 (七)- Message-Box 实现
从零实现 Vue 的组件库 (八)- Input 实现
来源: https://juejin.im/post/5c2d9a49f265da6169175ae7