共计 6598 个字符,预计需要花费 17 分钟才能阅读完成。
本篇文章记录仿写一个
el-switch
组件细节,从而有助于大家更好了解饿了么 ui 对应组件具体工作细节。本文是 elementui 源码学习仿写系列的又一篇文章,后续闲暇了会不断更新并仿写其余组件。源码在 github 上,大家能够拉下来,npm start 运行跑起来,联合正文有助于更好的了解。github 仓库地址如下:https://github.com/shuirongsh…
switch 组件思考
组件性能作用
switch 组件
个别是示意开关状态或者两种状态之间的切换,如点击开启网站的夜间模式,或敞开夜间模式。如下图 vue 官网首页就有这样的操作性能:
vue 官网链接地址:https://cn.vuejs.org/
组件的构造
switch 组件
的构造还是比较简单的,次要分为两局部:
switch 组件切换小圆点按钮
switch 组件切换容器
组件的实现思路
根本的 switch
切换布局构造
在实现 switch 组件
的时候,switch 组件切换容器
能够间接画一个div
去示意,那么 switch 组件切换小圆点按钮
咱们也须要画一个div
吗?其实不必的。
- 咱们能够应用
伪元素
先画出一个切换小圆点按钮
(联合定位) - 而后须要定义一个
标识布尔值
,用于更改切换组件开启敞开状态
- 当状态变动的时候,去更改
切换小圆点按钮
在左侧或在右侧的地位(通过 class),即实现了切换性能 - 再加上过渡成果,这样的话,
switch 组件
的切换(开启敞开)就会很丝滑了
开启敞开 switch 组件
的阐明文字性能注意事项
如下图:
- 对于开启时候文字在左侧,敞开时候文字在右侧,也开始能够通过弹性盒款式管制
justifyContent:flex-start / flex-end;
,当然动静padding
也要加上,详情见代码 - 若将文字退出切换框外部,那么就须要让切换框背景容器 dom 的宽度自适应,即依据内容文字的多少来管制,所以要提到
width: fit-content; 属性
(应用 fit-content 属性,让宽度随着内容文字多少自适应)
对于 fit-content 详情见官网文档:https://developer.mozilla.org…
给伪元素加上 hover 成果的写法
给伪元素加悬浮成果是先 hover 再::after(不要搞反了)
正确写法:.target:hover::after {background-color: red;}
谬误写法!!!:.target::after:hover {background-color: red;}
这里举一个例子代码效果图,复制粘贴即可应用,如下:
<!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>
对于封装的
mySwitch 组件
的其余的货色,联合笔者的正文,就能够清晰的了解了。这个组件次要还是款式的动态控制。另,笔者封装的组件暂不搭配
el-form
的校验应用,后续写到表单校验时,会补上并更新 github 上的代码仓库中当然局部写法成果,笔者的计划和官网的还是略有不同,毕竟思路略有不同,也倡议读者本人尝试仿写封装哦
封装的组件
效果图
笔者的 gif 录屏软件不太好,道友敌人们有没有不错的 gif 录制软件举荐一下 ^_^
复制粘贴即可应用哦
应用代码
<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>
封装代码
参考正文,倡议本人封装适宜本人公司业务的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>
留神
true-value 和 false-value
是官网自带的搭配 v -model 属性,其实这里不必也行,大家参考一下 antd 的组件便可明了。详见:https://cn.vuejs.org/guide/es…