目前,流行的UI组件库无非cube-ui、elementUI、iview…
一款优秀的组件库其实现必包含:性能、兼容、适应性等一系列指标。
今天笔者就给各位分析一下项目中常见的一个组件 —— 开关!及其实现。
笔者曾做过一个vue项目,其中用到cube-ui组件库,在里面,开关是这样实现的:
<cube-switch v-model="swich" class="abc" @input="inputHandle">
</cube-switch>
input事件是其内置事件:默认传参true/false —— 当前开关按钮的状态。
如图:
其实事后想想,在项目中一味的用现成组件库不是一个明智的选择——组件库良莠不齐,而且大多都有自己的限制条件,比如上面所说cube-ui就是在vue中使用的组件库。
这也是后来笔者写自己的UI库的原因 —— 下面实现的开关就截自那里:
开关的实现
上面我们可以知道需要实现的样式。这样的话,笔者认为最直接的方法莫过于:用两个元素分别充当开关的背景和开关的圆形按钮,再通过用不同的 class 来控制开关不同状态下的样式,甚至可以和菜单中的单选和多选功能一样:在开关里再隐藏一个 input 元素来记录开关的状态值。
这样的思路无论从效果上还是功能上来说都是可行的。但是不是笔者想要的 —— 否则的话也就不必写这篇文章了。。。
让我们再来分析一下组件库中的开关:点击滑动、需记录值、考虑性能。
结合这三点来看,我们完全可以只用一个 input 标签来实现:点击滑动可以用CSS3的@frames
实现、记录值这件事 input 本身就可以,至于第三点“考虑性能”…笔者在下面再为各位揭晓:
<div class="mxc-menu">
<a class="mxc-menu-item">
<p class="mxc-menu-name">这一行是文字说明,下面一行是重点</p>
<input class="mxc-switch" type="checkbox">
</a>
</div>
如上我们没有给任何样式,此时应该是这样的:
没错,默认的 input 元素就是个选择框,我们如果想把它改造成一个开关的样式,就要去掉它“默认”的样式:
-webkit-appearance: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
之后,这个 input 元素就变成了一个标准(普通)的盒子了。并且可以通过这个 input 是不是 checked 状态来区分盒子的(点击与否)样式:
.mxc-menu > .mxc-menu-item > .mxc-switch{
position: relative;
box-sizing: content-box;
width: 2.6rem;
height: 1.4rem;
border: 1px solid #ccc;
outline: 0;
border-radius: .75rem;
background-color: rgba(0,0,0,.1);
-webkit-appearance: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
.mxc-menu > .mxc-menu-item > .mxc-switch:checked{
border-color: #07C160;
background-color: #07C160;
}
在这段代码里,我们先是把 .mxc-switch 这个 input 元素当成容器来用 —— 虽然这种用法不是很常规,但 input 确实是可以这样使用的。
然后这里要注意下 checkbox 类型的 input 元素在有些浏览器里默认会是怪异盒模型,所以这里要【指定】成为标准盒模型,以便计算圆形按钮的大小和位置。
不管用户初始时是否选中了checkbox,都需要这样做 —— 反正没有坏处,你说呢
最后我们使用了 :checked 选择器来区分按钮的背景和边框。此时我们已经完成了1/3了。
接下来似乎就是重要的一步了:点击后元素上的小圆点来回移动!
因为笔者选择了“只用一个 input 标签”的方式实现,所以这里很自然能想到:CSS中的伪类选择器
事实上,只要给 ::after 元素加上属性“content:" ";”,就可以把它也当做一般的div来用了
.mxc-menu > .mxc-menu-item > .mxc-switch::after{
content:" ";
position: absolute;
top: 0;
left: 0;
width: 1.4rem;
height: 1.4rem;
border-radius: .7rem;
background-color: #FFFFFF;
box-shadow: 0 0 2px #999;
}
.mxc-menu > .mxc-menu-item > .mxc-switch:checked::after{
left: 1.2rem;
}
这段代码中需要注意的是:我们使用了 box-shadow 代替 border 来区分元素边界 —— 为了达到“立体”的效果。
box-shadow 属性是 CSS3 中规定的属性,是用来给盒子的边框添加阴影效果的。这个属性的语法是“box-shadow: px1 px2 px3 color;”,这个属性中几个值的作用如下:
px1 的值用来指定阴影的水平位移,可以是负值。
px2 用来指定阴影的竖直位移,也可以是负值。
px3 用来指定阴影的模糊半径,也就是阴影的模糊程度。
color 就是用来指定阴影的颜色。
在使用这个属性的时候有三点要注意:box-shadow 只是盒模型的一种显示效果,不会影响盒子的尺寸和位置;效果可叠加,中间用逗号隔开;属性消耗比较大,不建议大批量的使用。
此时我们已经完成了2/3。
现在点击这个开关,通过控制 checkbox 的选择情况就能直接控制开关的样式,不需要再手动的在元素上加什么 class。但是点击的时候就会发现,这个开关的效果非常生硬,所以我们需要给它加一些过渡效果。首先我们先来介绍下 CSS3 中的 transition 属性:
transition 属性也是 CSS3 中的属性,是用来指定元素的过渡效果的。比如某元素从样式1变成了样式2,如果没有过渡就是直接改变;如果使用了过渡效果的话,浏览器会计算这两个状态之间的所有状态,然后显示出变化过程。 transition 属性其实是四个属性的缩写,它们依次是:
transition-property,这个属性用来指定对哪个样式属性添加过渡效果。这个属性可以指定的通常是有数值的属性,比如 height、width、top、bottom、left、right、color等。如果希望对所有属性都添加过渡效果,那这个属性还可以使用“all”这个属性值,但不建议这么用。
transition-duration,这个属性用来指定完成过渡效果的时间。这个属性值的形式是“数字+s”,比如 .5s 就是 0.5 秒完成过渡效果。
transition-timing-function,这个属性用来指定过渡效果的变化速度的,它的取值可以是 linear、ease、ease-in、ease-out 和 ease-in-out 这几个具体的名称,也可以使用“cubic-bezier(n,n,n,n)”这种三阶贝塞尔函数。这里几种具体名称的取值实际上就是对几种固定参数的贝塞尔函数做了个别名,如果对贝塞尔函数感兴趣的话,可以自己去了解一下。
transition-delay,这个属性用来指定过渡效果的延时时间的,有了这个属性就可以允许过渡效果过一段时间后再开始。这个属性的取值也是一个时间格式的值,同 transition-duration 用法相同。
这样,我们给元素加上过渡效果即可。让我们改造一下上面的几段代码:
.mxc-menu > .mxc-menu-item > .mxc-switch{
position: relative;
box-sizing: content-box;
width: 2.6rem;
height: 1.4rem;
border: 1px solid #ccc;
outline: 0;
border-radius: .75rem;
background-color: rgba(0, 0, 0, 0.1);
transition: background-color .3s, border .3s;
-webkit-appearance: none;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.tt-menu > .tt-menu-item > .tt-switch::after{
content: " ";
position: absolute;
top: 0;
left: 0;
width: 1.4rem;
height: 1.4rem;
border-radius: .7rem;
background-color: #FFFFFF;
box-shadow: 0 0 2px #999;
transition: left .3s;
}
事实上,CSS中还有一个“有趣的事”:当你在初始样式上设置了transition,也就相当于设置了此元素的CSS动态效果【1】上的过渡特效!
最终效果:
【1】:即在当前元素的css上设置比如
:checked
、:hover
等使元素发生阳世上的改变的伪元素。