從零實現Vue的元件庫(九)- InputNumber 實現

Yzz發表於2019-01-04

基於 Input 元件進行二次擴充的 InputNumber 元件

InputNumer 元件的難點在於:
  • 實現滑鼠長按,計數器數值變動;
  • 導致 InputNumber 元件的值變化,有以下操作v-model繫結值的變化,加、減按鈕,input元件的輸入,需要對上述結果進行處理,所以如何設計合理模式,減少冗餘程式碼。

1. 例項

最終效果

程式碼

<!-- 基礎用法 -->
<fat-radio-group v-model="value">
    <fat-radio :value="1">備選項</fat-radio>
    <fat-radio :value="2">備選項</fat-radio>
</fat-radio-group>
<!-- 禁用狀態 -->
<fat-radio-group v-model="anotherValue">
    <fat-radio :value="1">備選項</fat-radio>
        <fat-radio :value="2" disabled>備選項</fat-radio>
</fat-radio-group>
<!-- 步數 -->
<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 prependslot 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 事件,當滑鼠還原時觸發。

然後,當滑鼠還原時,就會觸發 documentmouseup 事件,此時相應的處理為:

  • 記錄觸發時間為 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 的修改,同時劫持 getset 函式,在 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" />
複製程式碼

往期文章:

原創宣告: 該文章為原創文章,轉載請註明出處。

相關文章