終於遇到一個簡單的組件了,不過這個組件的實現仍是和我以前的實現有所不一樣,下圖Element的Switch組件css
看着就很簡單,是否是呀,官網代碼 點此關於開關組件,以前本身寫了一個,其實這個組件是不須要綁定任何click事件的,也就是說js部分幾乎能夠不寫,核心思想就是利用<input type=checkbox>
的checked屬性,當鼠標點擊input時,會切換其checked屬性,這是原生checkbox的特性。下面是本身實現的switch的htmlhtml
<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屬性就能辦到,以下vue
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實現滑塊的移動git
先來看switch的html結構github
<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這個方法就是切換開關的狀態bash
裏面先是一個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類的內容ui
@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以下this
@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%,至關於移動到了右側,而外層橢圓形背景的顏色也變化爲滑塊激活時的顏色
如今介紹下整個組件的數據傳遞邏輯,首先先來看用法
<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值