内容简介:终于遇到一个简单的组件了,不过这个组件的实现还是和我之前的实现有所不同,下图Element的Switch组件关于开关组件,之前自己写了一个,其实这个组件是不需要绑定任何click事件的,也就是说js部分几乎可以不写,核心思想就是里面的input用display:none隐藏掉,
终于遇到一个简单的组件了,不过这个组件的实现还是和我之前的实现有所不同,下图Element的Switch组件
之前自己的实现方式
关于开关组件,之前自己写了一个,其实这个组件是不需要绑定任何click事件的,也就是说js部分几乎可以不写,核心思想就是 利用 <input type=checkbox>
的checked属性 ,当鼠标点击input时,会切换其checked属性,这是原生checkbox的特性。下面是自己实现的switch的html
<template> <!--点击外层label,内层checkbox会发生改变--> <label class="switch"> <input type="checkbox" v-model="value" /> <!--内层滑动条,圆形按钮是after元素--> <div class="switch-checkbox"></div> </label> </template> 复制代码
里面的input用display:none隐藏掉, <div class="switch-checkbox"></div>
才是滑块的背景,用:after伪元素来模拟里面的圆形滑块,关键是在不写js的情况下如何控制滑块的左右移动,通过checked属性就能办到,如下
input[type="checkbox"] { //隐藏input display: none; //利用input的checked触发滑动动画 &:checked { //这里的+(相邻兄弟选择器)很重要,否则无法选择到,因为不加+就变成子元素 +.switch-checkbox { background-color:@activeBgColor; &:after { transform: translateX(26px); background: @activeButtonColor; opacity: 1!important; } } } } 复制代码
&:checked情况下用相邻兄弟选择器选择.switch-checkbox类里面的after伪元素,让其的transform发生改变,从而更改滑块的位置,这样就不用写一行js实现滑块的移动
Element的实现方式
先来看switch的html结构
<template> <div class="el-switch" :class="{ 'is-disabled': switchDisabled, 'is-checked': checked }" role="switch" :aria-checked="checked" :aria-disabled="switchDisabled" @click="switchValue" > <input class="el-switch__input" type="checkbox" @change="handleChange" ref="input" :id="id" :name="name" :true-value="activeValue" :false-value="inactiveValue" :disabled="switchDisabled" @keydown.enter="switchValue" > <!--前面的文字描述--> <span :class="['el-switch__label', 'el-switch__label--left', !checked ? 'is-active' : '']" v-if="inactiveIconClass || inactiveText"> <i :class="[inactiveIconClass]" v-if="inactiveIconClass"></i> <span v-if="!inactiveIconClass && inactiveText" :aria-hidden="checked">{{ inactiveText }}</span> </span> <!--开关核心部分--> <span class="el-switch__core" ref="core" :style="{ 'width': coreWidth + 'px' }"> </span> <!--后面的文字描述--> <span :class="['el-switch__label', 'el-switch__label--right', checked ? 'is-active' : '']" v-if="activeIconClass || activeText"> <i :class="[activeIconClass]" v-if="activeIconClass"></i> <span v-if="!activeIconClass && activeText" :aria-hidden="!checked">{{ activeText }}</span> </span> </div> </template> 复制代码
最外层一个div包裹,这是为了当有文字描述时,可以点击文字也触发开关状态改变,注意这个div上绑定了点击事件 @click="switchValue"
,这就和自己实现的方式不同了,Element的开关组件写了很多js,目的是能更好的控制一些特性实现,功能更丰富,可以猜到,switchValue这个方法就是切换开关的状态
里面先是一个input,这个input被隐藏掉,css代码如下
@include e(input) { position: absolute; width: 0; height: 0; opacity: 0; margin: 0; &:focus ~ .el-switch__core { outline: 1px solid $--color-primary; } } 复制代码
绝对定位且宽高都为0,也就是说无法点击到该input,然后是3个span并排下来,第一个和最后一个span都是文字描述,如果用户传入文字才显示,否则不显示,中间的span才是核心,很明显这个span是开关的外层椭圆背景,里面的滑块是由after伪元素实现
<span class="el-switch__core" ref="core" :style="{ 'width': coreWidth + 'px' }"> </span> 复制代码
看一下el-switch__core类的内容
@include e(core) { margin: 0; display: inline-block; position: relative; width: $--switch-width; height: $--switch-height; border: 1px solid $--switch-off-color; outline: none; border-radius: $--switch-core-border-radius; box-sizing: border-box; background: $--switch-off-color; cursor: pointer; transition: border-color .3s, background-color .3s; vertical-align: middle; &:after { content: ""; position: absolute; top: 1px; left: 1px; border-radius: $--border-radius-circle; transition: all .3s; width: $--switch-button-size; height: $--switch-button-size; background-color: $--color-white; } } 复制代码
开关外层的椭圆背景是display:inline-block且相对定位,因为里面的滑块要绝对定位,:after部分就是一个绝对定位的圆形, transition: all .3s
规定了滑块动画时间以及背景颜色变化的时间,但是换这个&:after只是滑块未激活状态,激活状态的css如下
@include when(checked) { .el-switch__core { border-color: $--switch-on-color; background-color: $--switch-on-color; &::after { left: 100%; margin-left: -$--switch-button-size - 1px; } } } 复制代码
可以看到激活状态下滑块的left值变为100%,相当于移动到了右侧,而外层椭圆形背景的颜色也变化为滑块激活时的颜色
Switch的js逻辑
现在介绍下整个组件的数据传递逻辑,首先先来看用法
<el-switch v-model="value2" active-color="#13ce66" inactive-color="#ff4949"> </el-switch> 复制代码
只需要给该组件的v-model设置一个data中的值即可,当开关开启关闭后整个value2会相应的变化,首先要知道组件的v-model用法,v-model就是@input和:value的简写,因此在组件内部要有一个value作为prop,具体看Vue官网。然后回到Switch,最外层的div绑定了click事件,代码如下
switchValue() { !this.switchDisabled && this.handleChange(); }, 复制代码
当组件不在禁用状态下时触发handleChange方法,handleChange如下
handleChange(event) { this.$emit('input', !this.checked ? this.activeValue : this.inactiveValue); this.$emit('change', !this.checked ? this.activeValue : this.inactiveValue); this.$nextTick(() => { // set input's checked property // in case parent refuses to change component's value this.$refs.input.checked = this.checked; }); }, 复制代码
这里主要看前2句,第一句是emit了一个input,同时将开关最新的值传递出去,这就是组件v-model的用法,必须指定一个 $emit('input')
来改变组件上v-model的值,否则无法更新用户传入的v-model,然后第二个 $emit
是组件添加的change事件,用于switch 状态发生变化时的回调函数,用户可以在这里面监测开关值改变了这一事件
!this.checked ? this.activeValue : this.inactiveValue
说明了如果不是激活状态,则传递出去activeValue,激活状态的值,这就是在切换状态了。那么this.checked是啥呢?来看一下
computed: { checked() { return this.value === this.activeValue; }, } 复制代码
原来是一个计算属性,当v-model的值和激活状态的值相同时就是checked状态,反之不是,这样当 this.$emit('input', !this.checked ? this.activeValue : this.inactiveValue)
后checked这个计算属性也就跟着变化了,那么问题来了,handleClick后是如何控制滑块的动画效果呢?因为这里没有写任何js,
这里通过$refs.input.checked拿到了内置input的checked的值(这里通过setAttribute也可以改,),注意到最外层的div
<div ... :class="{ 'is-disabled': switchDisabled, 'is-checked': checked }" @click="switchValue" > 复制代码
这里的class内有个is-checked类,它就是由checked这个计算属性控制,当checked为true时,div就添加这个is-checked类,这个类实际上啥都没有,作用是用来控制div里面滑块span的类以及after伪元素,如下
当有is-checked类时,上述css就被激活,因此改变了滑块背景的背景色和边框色,同时也改变了:after伪元素。
handleClick里面的nextTick那里不明白,有这么2句注释,这里将input的checked属性也改变了,是为了防止父组件拒绝修改value,为什么会拒绝修改呢,不太清楚
// set input's checked property // in case parent refuses to change component's value 复制代码
我试着将switch组件里面的所有input相关的内容都去掉,该组件仍然工作正常,说明input不是必须的,仔细想一下也对,上面的分析和input没有任何关系,组件内维护了activeValue和inactiveValue,通过v-model传入value,然后将value和activeValue相比来确定switch是否checked,确实不需要input
最后看一下created里面的内容
created() { if (!~[this.activeValue, this.inactiveValue].indexOf(this.value)) { this.$emit('input', this.inactiveValue); } }, 复制代码
这里说明当用户传入的v-model的值既不是activeValue也不是inactiveValue时,将inactiveValue传递出去,也就是让组件置位非开启状态,这个~代表按位非运算符,如果[this.activeValue, this.inactiveValue].indexOf(this.value)为-1,则按位非后变为0,再!后变为1,为true,则进if
再说下~~和!!,前者是用来将小数向下取整的操作(~对浮点数进行了截断),后者是用来将值变为bool值
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Black Box Society
Frank Pasquale / Harvard University Press / 2015-1-5 / USD 35.00
Every day, corporations are connecting the dots about our personal behavior—silently scrutinizing clues left behind by our work habits and Internet use. The data compiled and portraits created are inc......一起来看看 《The Black Box Society》 这本书的介绍吧!