本篇文章记录仿写一个el-switch组件细节,从而有助于大家更好了解饿了么ui对应组件具体工作细节。本文是elementui源码学习仿写系列的又一篇文章,后续闲暇了会不断更新并仿写其余组件。源码在github上,大家能够拉下来,npm start运行跑起来,联合正文有助于更好的了解。github仓库地址如下:https://github.com/shuirongsh...

switch组件思考

组件性能作用

switch组件个别是示意开关状态或者两种状态之间的切换,如点击开启网站的夜间模式,或敞开夜间模式。如下图vue官网首页就有这样的操作性能:

vue官网链接地址:https://cn.vuejs.org/

组件的构造

switch组件的构造还是比较简单的,次要分为两局部:

  1. switch组件切换小圆点按钮
  2. switch组件切换容器

组件的实现思路

根本的switch切换布局构造

在实现switch组件的时候,switch组件切换容器 能够间接画一个div去示意,那么 switch组件切换小圆点按钮 咱们也须要画一个div吗?其实不必的。

  1. 咱们能够应用伪元素先画出一个切换小圆点按钮(联合定位)
  2. 而后须要定义一个标识布尔值,用于更改切换组件开启敞开状态
  3. 当状态变动的时候,去更改切换小圆点按钮在左侧或在右侧的地位(通过class),即实现了切换性能
  4. 再加上过渡成果,这样的话,switch组件的切换(开启敞开)就会很丝滑了

开启敞开switch组件的阐明文字性能注意事项

如下图:

  1. 对于开启时候文字在左侧,敞开时候文字在右侧,也开始能够通过弹性盒款式管制 justifyContent:flex-start / flex-end;,当然动静padding也要加上,详情见代码
  2. 若将文字退出切换框外部,那么就须要让切换框背景容器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...