實現一個Slider元件,方便使用者通過拖動滑塊在一個固定區間內進行選擇,增強互動細節。
概述: 在使用者手動一些限定數字時,如果採用輸入框的形式,會需要提示資訊和錯誤資訊來引導使用者,這就存在一些冗餘操作。所以衍生出Slider元件,方便使用者拖動來選定一個值。
該元件的痛點在於:- 相容不同遊覽器的樣式;
- Slider元件的value、min、max之間的關係,以及對樣式的影響。
1. 例項
程式碼
<!-- 基礎用法 -->
<fat-slider />
<!-- 設定Min、Max以及Step -->
<fat-slider :min="20" :max="40" step="5" />
<!-- 繫結v-model -->
<fat-slider v-model="initValue" />
<!-- 不現實Tool-Tip -->
<fat-slider v-model="value" :show-tooltip="false" />
<!-- disabled 該元件 -->
<fat-slider disabled />
複製程式碼
例項地址:Slider 例項
程式碼地址:Github UI-Library
2. 原理
該元件的實現是基於原生的<input type="range" />
,再通過改寫樣式以達到上圖效果。
元件的基本結構如下
<template>
<div :class="['slider-wrap', { 'is-disabled': disabled }]">
<input
:class="['slider-inner', { 'is-disabled': disabled }]"
:disabled="disabled"
:min="min"
:max="max"
type="range"
v-on="$listeners"
v-model="rate"
v-bind="$attrs"
>
</div>
</template>
複製程式碼
- 先將修改元件的thumb以及軌道track的樣式:
將其從原生的形式
變成以下的樣式
基於cross-browser-range-input這篇博文,進行基礎樣式的修改。
為了相容不同的遊覽器,首先利用@mixin
抽離出thumb公共的樣式。
@mixin thumb-common-style() {
position: relative;
z-index: 3;
border: 2px solid #409eff;
background: #fff;
cursor: grab;
transition: all 0.3s;
}
// 後續可以豐富rect-slider-thumb等型別
@mixin circle-slider-thumb() {
width: 18px;
height: 18px;
border-radius: 50%;
&:active {
cursor: grabbing;
}
}
複製程式碼
然後適配不同的瀏覽器
&::-webkit-slider-thumb {
// thumb居中
margin-top: -6px;
@include circle-slider-thumb();
@include thumb-common-style();
}
&::-moz-range-thumb {
@include circle-slider-thumb();
@include thumb-common-style();
// z-index無效
transform: translateZ(1px);
}
&::-ms-thumb {
@include circle-slider-thumb();
@include thumb-common-style();
}
複製程式碼
然後以同樣的方法來處理track的樣式
@mixin common-track {
width: 100%;
height: 6px;
background: #e4e7ed;
border-radius: 3px;
cursor: pointer;
}
複製程式碼
適配不同瀏覽器
&::-webkit-slider-runnable-track {
@include common-track();
}
&::-moz-range-track {
@include common-track();
}
/* 只有ms支援fill-lower、fill-upper */
&::-ms-track {
width: 100%;
height: 6px;
cursor: pointer;
background: transparent;
border-color: transparent;
border-width: 16px 0;
color: transparent;
}
&::-ms-fill-lower {
background: #409eff;
border-radius: 3px;
}
&::-ms-fill-upper {
background: #c4c4c4;
border-radius: 3px;
}
複製程式碼
此時Slider元件在不同瀏覽器下的顯示,如下圖
-
Chrome
-
Firefox
-
Ie11
此時的Ie11優秀的不得了,不僅提供了fill-upper
、fill-lower
還自帶tool-tip提示功能。為了讓其他瀏覽器向他靠齊,就需要實現上述兩個功能。豐富元件的結構為
<div :class="['slider-wrapper', { 'is-disabled': disabled }]">
<div v-if="!isIE" class="progress" :style="progressStyle"></div>
<input
:class="['slider-inner', { 'is-disabled': disabled }]"
:disabled="disabled"
:min="min"
:max="max"
type="range"
v-on="$listeners"
v-model="rate"
v-bind="$attrs"
>
<span v-if="!isIE && showTooltip" class="tooltip" :style="toolTipPosition">
{{ rate }}</span>
</div>
複製程式碼
元件中progress
、tooltip
的樣式需要通過當前的rate值來進行修改,其規則為
computed: {
isIE() {
return (
!!window.ActiveXObject ||
"ActiveXObject" in window ||
navigator.userAgent.indexOf("Edge") > -1
);
},
progressStyle() {
const { rate, max, min } = this;
return {
width: `${((rate - min) * 100) / (max - min)}%`
};
},
toolTipPosition() {
const { rate, max, min } = this;
const xOffset = 9 - 18 * ((rate - min) / (max - min));
return {
left: `${((rate - min) * 100) / (max - min)}%`,
marginLeft: `${xOffset}px`,
transform: `translateX(-50%)`
};
}
}
複製程式碼
其中progressStyle
比較好理解,就是當前rate的值佔整體的百分比,而toolTipPosition
則是利用
position: absolute;
/* <percentage>s of the width of the containing block */
left: 10%;
/* <percentage>s of the width of the element */
tansform: translateX(-50%);
複製程式碼
3. 使用
進一步將其封裝成Vue的元件,配置其props
、data
export default {
props: {
showTooltip: { type: Boolean, default: true },
disabled: { type: Boolean, default: false },
min: { type: Number, default: 0 },
max: { type: Number, default: 100 },
value: { type: [Number, String] }
},
// 處理v-model
model: {
prop: "value",
event: "sliding"
},
watch: {
// 動態修改
rate(value) {
this.$emit("sliding", Number(value));
},
// 如果不存在初始值的話,以最小值為初始值
value: {
handler(value) {
this.rate = this.value || this.min;
},
immediate: true
}
}
}
複製程式碼
4. 總結
封裝一個Slider元件,相容<input type="range" />
在不同瀏覽器下的樣式,以及簡化其內部邏輯,方便後續擴充。
往期文章:
- 從零實現Vue的元件庫(零)- 基本結構以及構建工具
- 從零實現Vue的元件庫(一)- Toast 實現
- 從零實現Vue的元件庫(二)- Slider 實現
- 從零實現Vue的元件庫(三)- Tabs 實現
- 從零實現Vue的元件庫(四)- File-Reader 實現
- 從零實現Vue的元件庫(五)- Breadcrumb 實現
- 從零實現Vue的元件庫(六)- Hover-Tip 實現
- 從零實現Vue的元件庫(七)- Message-Box 實現
- 從零實現Vue的元件庫(八)- Input 實現
- 從零實現Vue的元件庫(九)- InputNumber 實現
- 從零實現Vue的元件庫(十)- Select 實現
- 從零實現Vue的元件庫(十一)- Date-picker 實現
- 從零實現Vue的元件庫(十二)- Table 實現
- 從零實現Vue的元件庫(十三)- Pagination 實現
- 從零實現Vue的元件庫(十四)- RadioGroup 實現
- 從零實現Vue的元件庫(十五)- CheckboxGroup 實現
原創宣告: 該文章為原創文章,轉載請註明出處。