组件库开发: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的文章~

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

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

相关推荐
low神11 分钟前
前端在网络安全攻击问题上能做什么?
前端·安全·web安全
码力码力我爱你1 小时前
QT + WebAssembly + Vue环境搭建
vue.js·vue·wasm·webassembly·emscripten
qbbmnnnnnn1 小时前
【CSS Tricks】如何做一个粒子效果的logo
前端·css
唐家小妹1 小时前
【flex-grow】计算 flex弹性盒子的子元素的宽度大小
前端·javascript·css·html
涔溪1 小时前
uni-app环境搭建
前端·uni-app
安冬的码畜日常1 小时前
【CSS in Depth 2 精译_032】5.4 Grid 网格布局的显示网格与隐式网格(上)
前端·css·css3·html5·网格布局·grid布局·css网格布局
洛千陨1 小时前
element-plus弹窗内分页表格保留勾选项
前端·javascript·vue.js
小小19921 小时前
elementui 单元格添加样式的两种方法
前端·javascript·elementui
完球了1 小时前
【Day02-JS+Vue+Ajax】
javascript·vue.js·笔记·学习·ajax
前端没钱1 小时前
若依Nodejs后台、实现90%以上接口,附体验地址、源码、拓展特色功能
前端·javascript·vue.js·node.js