组件库开发:yk-switch(开关)组件

switch(开关)组件

我们现在开始做switch组件,我们需要思考,一个常见的switch是什么样子的

我觉得,至少是上面这样的,满足基本的switch需求,然后才可以做更多的操作

那么,我们希望我们的switch组件有哪些功能呢

  • 绑定值
  • 禁用功能
  • 加载状态
  • 大小size

并且我们点击开关的时候会发生点击事件,切换开关状态

然后呢,我们可以添加额外的功能,比如自定义开关的背景色

好的,那我们有想法之后,就直接开始吧~

常规的switch

好的,目录结构我就不说啦,新建出一个swich的组件目录结构就行了

首先,我们需要知道,switch的原型是一个button,于是我们首先构建出一个button

html 复制代码
    <button type="button" role="switch"></button>

这里我们给一个typeroletype我相信大家是了解的,可能role用的不是很多

roleHTML5的一个属性,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)
}
  1. if (isDisabled.value) return:这个条件语句检查开关按钮是否被禁用。如果按钮被禁用(isDisabled.valuetrue),则直接返回,不执行后续的代码。
  2. currentValue.value = !isChecked.value ? props.checkedValue : props.uncheckedValue:这行代码根据当前的开关状态来更新currentValue的值。如果当前状态为未选中状态(!isChecked.valuetrue),则将currentValue的值设置为props.checkedValue,表示切换为选中状态;否则,将currentValue的值设置为props.uncheckedValue,表示切换为未选中状态。
  3. emit('update:modelValue', currentValue.value):这行代码触发了一个名为'update:modelValue'的自定义事件,并将currentValue.value作为参数传递给该事件。这个事件通常用于在组件内部更新modelValue属性的值。
  4. emit('change', currentValue.value, e):这行代码触发了一个名为'change'的自定义事件,并将currentValue.value和事件对象e作为参数传递给该事件。这个事件通常用于在开关状态发生改变时,通知父组件或执行其他操作。

然后我们通过watch侦听器来保持currentValueprops.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-designReact版本已经创建了,具体可以看yike在B站发的视频,更欢迎大家关注yike~

这是视频地址yike-design-react版前端UI框架--github开源项目期待你的加入_哔哩哔哩_bilibili
这是github仓库ecaps1038/yike-design-react (github.com)

在vue版本完事之后,我也会加入React的开发,并尽力给大家出React的文章~

再次抱歉更新频率较慢 ~ 希望大家海涵!

还有大家有想了解的组件,可以评论,我会提前书写文章,也欢迎大家提建议~

相关推荐
张龙6875 分钟前
RAG 知识库问答系统实战:分块、混合检索、RRF 融合与重排全链路拆解
前端
用户1306095607235 分钟前
第二章学完后,我对 webpack5 前端工程化的一点理解
前端
a1117767 分钟前
小鸡下蛋 小游戏 html
前端·开源·html
码哥DFS14 分钟前
算法练习day1-备战2027届秋招
前端·javascript·数据结构·算法
Bigger17 分钟前
因为一句「华流才是最屌的」,我做了一个中国风设计的 Skill
前端·人工智能·设计
别怪我很水26 分钟前
Vue element admin 浏览器本地存储 localStorage、useStorage
前端·javascript·vue.js
手揽回忆怎么睡26 分钟前
Ubuntu 22.04 64位安装MinIO
linux·前端·ubuntu
铁皮饭盒29 分钟前
网页端OCR, 加载6mb大小模型, 又快又准, 百度这次真香
前端·后端
Web极客码1 小时前
如何在WordPress中添加动态内容
服务器·前端·wordpress
云浪1 小时前
给 AI Agent 加上语音交互:ASR + 流式 TTS 实战
前端·人工智能·后端