本篇文章记录仿写一个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...