前言
开关组件是日常开发中常见的一个组件,公司产品中也用到了开关组件,使用的是ElementUI中的el-switch,但是el-switch的描述文字是放到开关外面的,项目UI要求描述文字在里面,只能想办法将描述文字放到开关里面,本节我们来看一下具体的实现思路。
思路
经过搜索思路,得出了三种思路,下面我们一个个地看下具体实现思路。
更换组件库
如果有封装好的组件满足描述文字在里面,直接拿来使用,这是最简单,刚好el的升级版el-plus,实现了将描述文字放到开关里面。plus中新增了inline-prompt,设置为ture时描述文本就会在里面。
js
<el-switch v-model="flag" inline-prompt active-text="你好" inactive-text="世界" />
一个属性直接搞定,如果我们是一个新项目并且用的vue3那么可以直接将组件升级成plus,老项目就不适用。
修改样式
el-switch描述文字在外面,我们修改样式把文字定位到开关里面,根据不同的值去切换里面的值。打开页面我们可以看到描述文字与按钮的class名,通过class名称将描述文字定位到按钮中。
我们将中间按钮设置position为relative,将左右两边pisition设置为absolute,并且进行位置移动,将描述文字放到按钮里。
css
.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的属性值。
js
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里面,那么描述文本不就放到了按钮当中,非常的简单。
js
<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,毕竟一个页面可能存在多个按钮。
js
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去累加基础宽度。
js
// 计算宽度
get minCoreWidth() {
const maxTextLen = Math.max(
this.activeText.length,
this.inactiveText.length
)
return `calc(${maxTextLen}em + 30px)`
}
剩下的就是根据check去动态修改样式,我们可以根据自己的需求去设置props跟样式。
总结
如果我们碰到描述文本在内部的开关,并且不能随意升级的话,可以尝试自己去封装一个组件,本质上还是在现有组件基础上进行二次封装。