This article records and imitates a el-switch
component details, which will help you better understand the specific working details of the corresponding components of Ele.me ui. This article is another article in the elementui source code learning and imitation series. I will continue to update and imitate other components when I am free. The source code is on github, you can pull it down, npm start to run, and comments are helpful for better understanding. The github warehouse address is as follows: https://github.com/shuirongshuifu/elementSrcCodeStudy
switch component thinking
component function
switch组件
generally means the switch state or the switch between two states, such as clicking to turn on the night mode of the website, or turn off the night mode. As shown in the figure below, the homepage of the Vue official website has such an operation function:
Vue official website link address: https://cn.vuejs.org/
Component structure
switch组件
The structure is relatively simple, mainly divided into two parts:
-
switch组件切换小圆点按钮
-
switch组件切换容器
Component implementation ideas
basic switch
switch layout structure
switch组件
的时候, switch组件切换容器
div
去表示,那么switch组件切换小圆点按钮
div
? Actually no need.
- We can use
伪元素
to draw a first切换小圆点按钮
(combined with positioning) - Then you need to define a
标识布尔值
for changing切换组件开启关闭状态
- When the state changes, go to change
切换小圆点按钮
on the left or on the right position (by class), that is, to realize the switching function - Coupled with the transition effect, in this case, the switching (on and off) of
switch组件
will be very smooth
On and off the description text function of switch组件
Notes
As shown below:
- Regarding the text on the left when opening, and the text on the right when closing, it can also be controlled by the elastic box style
justifyContent:flex-start / flex-end;
, of course dynamicpadding
should also be added, see the code for details - If you add text to the inside of the switch box, you need to make the width of the background container dom of the switch box adaptive, that is, to control it according to the amount of content text, so it is necessary to mention
width: fit-content;属性
(using the fit-content attribute, let The width adapts to the content of the text)
For details about fit-content, see the official documentation: https://developer.mozilla.org/en-US/docs/Web/CSS/fit-content
Add hover effect to pseudo-elements
给伪元素加悬浮效果是先hover再::after(不要搞反了)
Correct spelling: .target:hover::after { background-color: red; }
Wrong spelling!!!: .target::after:hover { background-color: red; }
Here is an example code rendering, which can be used by copying and pasting, as follows:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
padding: 120px;
}
.target {
display: inline-block;
width: 60px;
height: 18px;
background-color: #c4c4c4;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s;
position: relative;
}
/* 使用伪元素画一个小圆点 */
.target::after {
content: "";
position: absolute;
top: -4px;
left: -2px;
border-radius: 50%;
width: 24px;
height: 24px;
border: 1px solid #e9e9e9;
box-shadow: 0 1px 10px 0 rgba(0, 0, 0, 0.3);
background-color: #fff;
transition: all 0.3s;
}
/* 给自己加悬浮效果直接写即可 */
.target:hover {
background-color: green;
}
/* 给伪元素加悬浮效果是先hover再::after(不要搞反了) */
.target:hover::after {
background-color: red;
}
</style>
</head>
<body>
<div class="target"></div>
</body>
</html>
Other things about the package
mySwitch组件
can be clearly understood with the author's comments. This component is mainly for dynamic control of styles.In addition, the components encapsulated by the author are not used for the verification of
el-form
. When the form verification is written later, it will be added and updated in the code repository on github.Of course, the author's plan is slightly different from the official one. After all, the ideas are slightly different, and readers are also advised to try to imitate the package by themselves.
packaged components
renderings
The author's gif screen recording software is not very good. Do you have any good gif recording software to recommend ^_^
Copy and paste to use
use code
<template>
<div>
<my-divider lineType="dotted" content-position="left">普通使用</my-divider>
<my-switch @change="change" v-model="flag1"></my-switch>
<my-switch v-model="flag2"></my-switch>
<my-divider lineType="dotted" content-position="left"
>开启关闭文字</my-divider
>
<my-switch v-model="flag3" openText="开启啦开启啦" closeText="关闭了"></my-switch>
<my-switch v-model="flag3" openText="ON" closeText="OFF"></my-switch>
<my-switch v-model="flag3" openText="✔" closeText="✘"></my-switch>
<my-divider lineType="dotted" content-position="left"
>自定义开启关闭背景色</my-divider
>
<my-switch
v-model="flag4"
active-color="#19be6b"
inactive-color="#ed4014"
></my-switch>
<my-divider lineType="dotted" content-position="left">禁用</my-divider>
<my-switch v-model="flag5" disabled></my-switch>
<my-switch v-model="flag6" disabled></my-switch>
<br />
<my-divider lineType="dotted" content-position="left"
>small切换框</my-divider
>
<my-switch
v-model="flag7"
active-color="#006CFF"
inactive-color="#DD6DA6"
openText="small"
closeText="switch"
size="small"
></my-switch>
<my-divider lineType="dotted" content-position="left">big切换框</my-divider>
<my-switch
v-model="flag8"
active-color="#2F2F2F"
inactive-color="#ddd"
openText="☾"
closeText="☼"
size="big"
></my-switch>
</div>
</template>
<script>
export default {
data() {
return {
flag1: true,
flag2: false,
flag3: true,
flag4: true,
flag5: false,
flag6: true,
flag7: true,
flag8: true,
};
},
methods: {
change(val) {
console.log("切换后的状态", val);
},
},
};
</script>
package code
Referring to the notes, it is recommended to encapsulate the one suitable for your company's business switch组件
<template>
<div
class="mySwitchWrap"
:class="[disabled ? 'disabledSwitch' : '', size]"
@click="changeStatus"
>
<!-- input标签 -->
<input
class="switchInput"
type="checkbox"
@change="changeStatus"
ref="input"
:true-value="activeValue"
:false-value="inactiveValue"
:disabled="disabled"
@keydown.enter="changeStatus"
/>
<!-- 主要内容 -->
<span
:class="[
'switchCentre',
'circleDotLeft',
isOpen ? 'changeCircleDotRight' : '',
]"
:style="{
background: computedBackground,
borderColor: computedBackground,
}"
>
<span
class="text"
:style="{
justifyContent: isOpen ? 'flex-start' : 'flex-end',
padding: isOpen ? '0 28px 0 8px' : '0 8px 0 28px',
}"
>{{ isOpen ? openText : closeText }}</span
>
</span>
</div>
</template>
<script>
export default {
name: "mySwitch",
props: {
openText: String,
closeText: String,
// v-model搭配value接收数据,this.$emit("input", val)更新数据
value: {
type: Boolean,
default: false, // 默认false
},
// 是否禁用,默认不禁用
disabled: {
type: Boolean,
default: false,
},
// switch打开时为true
activeValue: {
type: Boolean,
default: true,
},
// switch关闭时为false
inactiveValue: {
type: Boolean,
default: false,
},
// 自定义switch打开时背景色
activeColor: {
type: String,
default: "",
},
// 自定义switch关闭时背景色
inactiveColor: {
type: String,
default: "",
},
// switch切换框的大小
size: {
type: String,
default: "",
},
},
computed: {
// 是否打开切换框取决于外层传递的v-model的值是否为true
isOpen() {
return this.value === this.activeValue;
},
computedBackground() {
// 若传递了激活颜色和未激活颜色,就根据是否开启状态使用传递的颜色
if ((this.activeColor.length > 0) & (this.inactiveColor.length > 0)) {
return this.isOpen ? this.activeColor : this.inactiveColor;
}
// 没传递就根据开启使用默认的背景色
else {
return this.isOpen ? "#409EFF" : "#C0CCDA";
}
},
},
methods: {
changeStatus() {
// 禁用情况下,不做状态更改切换
if (this.disabled) {
return;
}
// 首先看是否开启,若开启,就传递不开启;若不开启,就传递开启(因为状态切换,取反)
const val = this.isOpen ? this.inactiveValue : this.activeValue;
this.$emit("input", val); // 更新外层v-model绑定的值
this.$emit("change", val); // 抛出一个change事件以供用户使用
},
},
};
</script>
<style scoped lang="less">
.mySwitchWrap {
display: inline-block;
cursor: pointer;
font-size: 14px;
margin: 2px;
/* 将input标签隐藏起来,宽高都为0,透明度也为0,看不到 */
.switchInput {
position: absolute;
width: 0;
height: 0;
opacity: 0;
margin: 0;
}
.switchCentre {
display: inline-block;
width: auto;
height: 20px;
color: #fff;
background-color: #c4c4c4;
border: 1px solid;
outline: 0;
border-radius: 10px;
box-sizing: border-box;
transition: all 0.3s; // 加上过渡效果
position: relative;
.text {
min-width: 54px; // 设置最小宽度
width: fit-content; // 使用fit-content属性,让宽度随着内容多少自适应
height: 100%;
font-size: 12px;
display: flex;
// justify-content: justifyContent; // 注意,这里也是通过:style控制文字靠左还是靠右
align-items: center;
transition: all 0.3s; // 加上过渡效果
}
}
// 默认小圆点在左侧的(使用伪元素创建一个小圆点)
.circleDotLeft::after {
content: "";
position: absolute;
top: -4px;
left: -2px;
border-radius: 100%;
width: 24px;
height: 24px;
border: 1px solid #e9e9e9;
box-shadow: 0 1px 10px 0 rgba(0, 0, 0, 0.3); // 原来小圆点有一点阴影
background-color: #fff;
transition: all 0.3s; // 加上过渡效果
}
// 当switch打开时,加上类名~通过更改定位left值控制圆点在右侧
.changeCircleDotRight::after {
left: 100%;
margin-left: -24px;
}
// 悬浮加重小圆点阴影
.circleDotLeft:hover::after {
box-shadow: 0 1px 18px 0 rgba(0, 0, 0, 0.5);
}
}
// 除了cursor样式的not-allowed还要搭配js判断才禁用到位
.disabledSwitch {
cursor: not-allowed;
opacity: 0.48;
}
// 禁用情况下,保持小圆点原有阴影
.disabledSwitch .circleDotLeft:hover::after {
box-shadow: 0 1px 10px 0 rgba(0, 0, 0, 0.3);
}
// 小型switch组件做一个缩放
.small {
zoom: 0.8;
}
// 大型switch组件做一个缩放
.big {
zoom: 1.6;
}
</style>
Note true-value和false-value
is the official v-model attribute that comes with it. In fact, it is not necessary here. You can see it by referring to the components of antd. See: https://cn.vuejs.org/guide/essentials/forms.html#checkbox-2
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。