乐趣区

关于element-ui:elementui源码学习之仿写一个elswitch

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

退出移动版