switch(开关)组件
我们现在开始做switch
组件,我们需要思考,一个常见的switch
是什么样子的
我觉得,至少是上面这样的,满足基本的switch
需求,然后才可以做更多的操作
那么,我们希望我们的switch
组件有哪些功能呢
- 绑定值
- 禁用功能
- 加载状态
- 大小size
并且我们点击开关的时候会发生点击事件
,切换开关状态
然后呢,我们可以添加额外的功能,比如自定义开关的背景色
好的,那我们有想法之后,就直接开始吧~
常规的switch
好的,目录结构我就不说啦,新建出一个swich
的组件目录结构就行了
首先,我们需要知道,switch
的原型是一个button
,于是我们首先构建出一个button
html
<button type="button" role="switch"></button>
这里我们给一个type
和role
,type
我相信大家是了解的,可能role
用的不是很多
role
是HTML5
的一个属性,role
属性用于指定元素的角色或作用,以提供有关元素的附加语义信息,特别是对于辅助技术和可访问性非常重要,但是需要注意的是,role
属性并不会自动改变元素的行为或外观,它只是提供了附加的语义信息
props、emit
好的,现在我们根据功能开始规范一下props
- modelValue:绑定值
- size:开关的大小
- loading:是否为加载中状态
- disabled:是否禁用
- checkedValue:选中时的值
- uncheckedValue:未选中时的值
- checkedColor:选中时的开关背景色
- uncheckedColor:未选中时的开关背景色
ts
export type SwitchProps = {
modelValue: boolean | string | number;
size?: 's' | 'm';
loading?: boolean;
disabled?: boolean;
checkedValue?: boolean | string | number;
uncheckedValue?: boolean | string | number;
checkedColor?: string;
uncheckedColor?: string;
};
除了props
的内容,,因为我们的switch
组件会有向外传值
的动作我们也可以规定一下未来emit
的内容
ts
export const switchEmits = {
'update:modelValue': (value: boolean | string | number) => true,
change: (value: boolean | string | number, ev: Event) => true,
};
switch
模板结构
html
<button type="button" role="switch" :class="classes" :style="styles" :disabled="isDisabled" @click="handleClick">
<span :class="`yk-switch-dot`">
<svg v-if="loading" viewBox="25 25 50 50">
<circle r="20" cy="50" cx="50"></circle>
</svg>
</span>
</button>
好的,我们现在完善一下我们switch
的模板结构,然后我们解析一下
:class="classes"
动态绑定了按钮的 CSS 类。classes
是一个计算属性,用于控制按钮的样式,根据按钮的不同状态(是否选中、是否加载中)动态改变。
:style="styles"
动态绑定了按钮的内联样式。styles
是一个计算属性用于控制按钮的背景颜色。根据按钮的不同状态,背景颜色会被设置为选中或未选中的颜色。
:disabled="isDisabled"
动态绑定了按钮的禁用状态。isDisabled
也是一个计算属性,根据props.disabled
(是否禁用)和props.loading
(是否加载中)的值来确定按钮是否应该被禁用。
@click="handleClick"
绑定了按钮的点击事件。当按钮被点击时,会触发handleClick
方法,也就是进行开关的切换
以及向外传值
的操作。
<span :class="
yk-switch-dot">
定义了一个<span>
元素,用于包裹开关按钮的显示内容。也就是我们开关的滑块
,这里使用了动态绑定的类名yk-switch-dot
。
svg
就是我们的loading
了
script
好的,现在我们完善一下逻辑
首先我们初始化props默认值
,并通过计算属性计算出动态的属性
ts
defineOptions({
name: 'YkSwitch',
})
const props = withDefaults(defineProps<SwitchProps>(), {
modelValue: false,
size: 'm',
loading: false,
disabled: false,
checkedValue: true,
uncheckedValue: false,
})
const currentValue = ref(props.modelValue)
const isChecked = computed(() => currentValue.value === props.checkedValue)
const isDisabled = computed(() => props.disabled || props.loading)
const classes = computed(() => {
return [
'yk-switch',
`yk-switch--${props.size}`,
{
['yk-switch--checked']: isChecked.value,
['yk-switch--loading']: props.loading,
},
]
})
const styles = computed(() => ({
backgroundColor: currentValue.value
? props.checkedColor
: props.uncheckedColor,
}))
好的,然后就是我们的事件处理了,首先我们这回可以先初始化emit
ts
const emit = defineEmits(switchEmits)
然后再写我们的事件
ts
const handleClick = (e: Event) => {
if (isDisabled.value) return
currentValue.value = !isChecked.value
? props.checkedValue
: props.uncheckedValue
emit('update:modelValue', currentValue.value)
emit('change', currentValue.value, e)
}
if (isDisabled.value) return
:这个条件语句检查开关按钮是否被禁用。如果按钮被禁用(isDisabled.value
为true
),则直接返回,不执行后续的代码。currentValue.value = !isChecked.value ? props.checkedValue : props.uncheckedValue
:这行代码根据当前的开关状态来更新currentValue
的值。如果当前状态为未选中状态(!isChecked.value
为true
),则将currentValue
的值设置为props.checkedValue
,表示切换为选中状态;否则,将currentValue
的值设置为props.uncheckedValue
,表示切换为未选中状态。emit('update:modelValue', currentValue.value)
:这行代码触发了一个名为'update:modelValue'
的自定义事件,并将currentValue.value
作为参数传递给该事件。这个事件通常用于在组件内部更新modelValue
属性的值。emit('change', currentValue.value, e)
:这行代码触发了一个名为'change'
的自定义事件,并将currentValue.value
和事件对象e
作为参数传递给该事件。这个事件通常用于在开关状态发生改变时,通知父组件或执行其他操作。
然后我们通过watch
侦听器来保持currentValue
与props.modelValue
的数据同步
ts
watch(
() => props.modelValue,
(value) => {
currentValue.value = value
},
)
好的,那我们的组件代码写完了,剩下的就是样式了
样式
我不会一条条带着大家写样式,所以这里,我们首先把样式给出来,然后我会解释一下重点的样式
less
@import '../../../styles/color/colors.less';
.yk-switch {
position: relative;
overflow: hidden;
padding: 0;
border: none;
background-color: @bg-color-ss;
outline: none;
transition: background-color 0.2s ease-in-out;
box-sizing: border-box;
vertical-align: middle;
cursor: pointer;
&--m {
min-width: 40px;
height: 22px;
line-height: 22px;
border-radius: 11px;
}
&--s {
min-width: 32px;
height: 18px;
line-height: 18px;
border-radius: 10px;
}
&-dot {
position: absolute;
top: 3px;
left: 3px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
color: #fff;
background-color: #fff;
transition: all 0.2s ease-in-out;
}
&--m &-dot {
width: 16px;
height: 16px;
}
&--s &-dot {
width: 12px;
height: 12px;
}
&--checked {
background-color: @pcolor;
}
&--m&--checked &-dot {
left: calc(100% - 19px);
}
&--s&--checked &-dot {
left: calc(100% - 15px);
}
&[disabled] {
opacity: 0.4;
cursor: not-allowed;
}
}
.yk-switch--loading {
opacity: 0.7 !important;
svg {
width: 16px;
transform-origin: center;
animation: rotate4 2s linear infinite;
}
circle {
fill: none;
stroke: hsl(208.83, 100%, 54.71%);
stroke-width: 3;
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
stroke-linecap: round;
animation: dash4 1.5s ease-in-out infinite;
}
@keyframes rotate4 {
100% {
transform: rotate(360deg);
}
}
@keyframes dash4 {
0% {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 90, 200;
stroke-dashoffset: -35px;
}
100% {
stroke-dashoffset: -125px;
}
}
}
.yk-switch
:开关按钮的基础样式
&--m
、&--s
,是对应m和s大小的样式
&-dot
:开关按钮上的圆点样式
&--checked
:开关按钮被选中时的样式。
&[disabled]
:开关按钮被禁用时的样式,其中的cursor: not-allowed;
可以设置鼠标指针为禁止符号。
.yk-switch--loading
:加载状态下的开关按钮样式。
svg
:内部的SVG图标样式,其中的transform-origin: center
设置变换原点为中心点。animation: rotate4 2s linear infinite
应用名为rotate4
的旋转动画,持续时间为2秒,线性变化,并无限循环。
circle
:内部的圆圈样式。
其中:
-
stroke: hsl(208.83, 100%, 54.71%);
:设置描边颜色为指定的HSL颜色值。 -
stroke-width: 3;
:设置描边宽度为3像素。 -
stroke-dasharray: 1, 200;
:设置虚线样式,由1个实线和200个空白组成。 -
stroke-dashoffset: 0;
:设置虚线起始偏移量为0。 -
stroke-linecap: round;
:设置线段端点为圆形。 -
animation: dash4 1.5s ease-in-out infinite;
:应用名为dash4
的虚线动画,持续时间为1.5秒,缓入缓出,并无限循环。 -
@keyframes rotate4
:定义名为rotate4
的旋转动画。-
100%
:在动画的最后一帧(100%)时的样式。transform: rotate(360deg);
:将元素顺时针旋转360度。
-
-
@keyframes dash4
:定义名为dash4
的虚线动画。-
0%
:在动画的第一帧(0%)时的样式。stroke-dasharray: 1, 200;
:设置虚线样式,由1个实线和200个空白组成。stroke-dashoffset: 0;
:设置虚线起始偏移量为0。
-
50%
:在动画的一半(50%)时的样式。stroke-dasharray: 90, 200;
:设置虚线样式,由90个实线和200个空白组成。stroke-dashoffset: -35px;
:设置虚线起始偏移量为-35像素。
-
100%
:在动画的最后一帧(100%)时的样式。stroke-dashoffset: -125px;
:设置虚线起始偏移量为-125像素
-
使用
然后最后我们按照之前的方式进行注册
,然后在demo
中进行使用即可
html
<template>
<div>
<yk-switch v-model="isChecked"></yk-switch>
<br>
<yk-switch v-model="unchecked" loading></yk-switch>
<br>
<yk-switch v-model="checked" loading></yk-switch>
<br>
<yk-switch v-model="isChecked" checked-color="#1fb4a2" unchecked-color="#fa5247"></yk-switch>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const isChecked = ref(false)
const unchecked = ref(false)
const checked = ref(true)
</script>
尾语
总的来说,switch
组件的开发是并不复杂的
简单来说,是把一个button
的内容进行滑块化
,然后把样式完善,基本的switch
就写好了
当然,还需要注意具体的逻辑和传值
的操作
很抱歉更新较慢,因为最近事情较多,而且我现在一部分时间在用webcomponents(Lit的方式)
去同步组件库,所以写文章的时间较少,同时yk-design
目前我们在完善的过程中,期待尽快地发版供大家使用
还有一点是,因大家广泛的要求,yk-design
的React
版本已经创建了,具体可以看yike
在B站发的视频,更欢迎大家关注yike
~
这是视频地址
:yike-design-react版前端UI框架--github开源项目期待你的加入_哔哩哔哩_bilibili
这是github仓库
:ecaps1038/yike-design-react (github.com)
在vue版本完事之后,我也会加入React的开发,并尽力给大家出React的文章~
再次抱歉更新频率较慢 ~ 希望大家海涵!
还有大家有想了解的组件,可以评论,我会提前书写文章,也欢迎大家提建议~