likes
comments
collection
share

需求小能手——嵌入文本的开关按钮

作者站长头像
站长
· 阅读数 22

前言

开关组件是日常开发中常见的一个组件,公司产品中也用到了开关组件,使用的是ElementUI中的el-switch,但是el-switch的描述文字是放到开关外面的,项目UI要求描述文字在里面,只能想办法将描述文字放到开关里面,本节我们来看一下具体的实现思路。

思路

经过搜索思路,得出了三种思路,下面我们一个个地看下具体实现思路。

更换组件库

如果有封装好的组件满足描述文字在里面,直接拿来使用,这是最简单,刚好el的升级版el-plus,实现了将描述文字放到开关里面。plus中新增了inline-prompt,设置为ture时描述文本就会在里面。

   <el-switch v-model="flag" inline-prompt active-text="你好" inactive-text="世界" />

需求小能手——嵌入文本的开关按钮 一个属性直接搞定,如果我们是一个新项目并且用的vue3那么可以直接将组件升级成plus,老项目就不适用。

修改样式

el-switch描述文字在外面,我们修改样式把文字定位到开关里面,根据不同的值去切换里面的值。打开页面我们可以看到描述文字与按钮的class名,通过class名称将描述文字定位到按钮中。

需求小能手——嵌入文本的开关按钮 我们将中间按钮设置position为relative,将左右两边pisition设置为absolute,并且进行位置移动,将描述文字放到按钮里。

   .el-switch__core {
    position: relative;
  }
  .is-active {
    color: aliceblue !important;
  }
  .el-switch__label--left {
    position: absolute;
    top: 0;
    left: 20px;
    z-index: 9;
  }
  .el-switch__label--right {
    position: absolute;
    top: 0;
    right: 20px;
    display: none;
  }

注意,我们假设初始化的值为false,这时候显示的是--left的内容,所以把--righ的display设置为none,此时false的描述文字就在按钮当中,接下来我们在按钮发生变化时,修改两者的display值。当按钮为开时,--right显示,--left隐藏;当按钮为关时,刚好相反。根据className获取对应的dom,然后设置display的属性值。

   handleSwitchChange(val) {
    const leftE = document.getElementsByClassName('el-switch__label--left')[0]
    leftE.style.display = val ? 'none' : 'block'
    const rightE = document.getElementsByClassName('el-switch__label--right')[0]
    rightE.style.display = val ? 'block' : 'none'
  }

当我们修改开关时,按钮内部就会出现对应的文字。该方法虽然达到我们的要求,但是限制比较多,不能很好地组件化。

自定义组件

没有工具创造工具,我们自定义一个可以将描述文本放到开关中的组件,当然上述两种思路我们也可以用到自定义组件的开发中。我们在修改样式的时候看到过开关的dom结构,所以我们直接用el-switch的样式以及对应的dom结构去封装,之所以不直接封装el-switch,因为el当中没有对应属性,不如直接用h5标签方便。 首先完成开关布局,根据el-switch页面标签,我们可以得出开关是input标签生成的,它里面有三个span子标签:

  • label--left显示关闭描述文本。
  • label--right显示开启描述文本。
  • core显示中间的按钮。

我们把left与right放到core里面,那么描述文本不就放到了按钮当中,非常的简单。

     <span
        class="el-switch__core inner-mode"
        ref="core"
        :class="{ 'is-checked': checked }"
        :style="{ 'width': coreWidth + 'px', 'min-width': minCoreWidth }"
      >
        <span v-if="!checked && inactiveText" :aria-hidden="checked">{{ inactiveText }}</span>
        <span v-else-if="checked && activeText" :aria-hidden="!checked">{{ activeText }}</span>
      </span>

上面的样式直接沿用el-switch的原有样式,并且我们可以自定义一个innerMode属性去控制是否将描述文本放到其中,当为ture时显示上述代码,当为false时还是el-switch的dom结构。到此我们的功能大致已经得到了,接下来就是一些细节处理,比如给每个开关按钮生成一个唯一的key,毕竟一个页面可能存在多个按钮。

   function guid() {
      return 'xxxxxxxx'.replace(/[xy]/g, function (c) {
        const r = (Math.random() * 16) | 0
        const v = c === 'x' ? r : (r & 0x3) | 0x8
        return v.toString(16)
      })
      }

还有一个需要注意的是按钮的宽度,我们需要把描述文本完全展示出来,所以按钮的宽度最好能跟着文字多少去变动。我们声明一个变量表示按钮的最小宽度,该值就由文字的多少动态的计算出来,通过比较开关描述文本得到最大宽度,然后按照一个字1px去累加基础宽度。

   // 计算宽度
   get minCoreWidth() {
    const maxTextLen = Math.max(
      this.activeText.length,
      this.inactiveText.length
    )
    return `calc(${maxTextLen}em + 30px)`
  }

剩下的就是根据check去动态修改样式,我们可以根据自己的需求去设置props跟样式。

总结

如果我们碰到描述文本在内部的开关,并且不能随意升级的话,可以尝试自己去封装一个组件,本质上还是在现有组件基础上进行二次封装。