实现一个Slider组件,方便用户通过拖动滑块在一个固定区间内进行选择,增强交互细节。
概述: 在用户手动一些限定数字时,如果采用输入框的形式,会需要提示信息和错误信息来引导用户,这就存在一些冗余操作。所以衍生出Slider组件,方便用户拖动来选定一个值。
该组件的痛点在于:- 兼容不同游览器的样式;
- Slider组件的value、min、max之间的关系,以及对样式的影响。
1. 实例
代码
<fat-slider />
<fat-slider :min="20" :max="40" step="5" />
<fat-slider v-model="initValue" />
<fat-slider v-model="value" :show-tooltip="false" />
<fat-slider disabled />
复制代码
实例地址:Slider 实例
代码地址:Github UI-Library
2. 原理
该组件的实现是基于原生的,再通过改写样式以达到上图效果。
组件的基本结构如下
<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;
/* s of the width of the containing block */
left: 10%;
/* 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组件,兼容在不同浏览器下的样式,以及简化其内部逻辑,方便后续拓展。
往期文章:
- 从零实现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 实现
原创声明: 该文章为原创文章,转载请注明出处。