VUE3+CSS变量模拟组件库switch

1,324 阅读1分钟

VUE+CSS变量模拟组件库switch开关

1.HTML结构:

<template>

​    *<!-- 父相子绝定位,父元素添加点击事件 -->*

    <div class="switch" ref="switchElement" @click="clickHandler">

        <div ref="icon" class="icon"></div>

​    </div>

</template>

2.CSS样式:

<style scoped>
/* 
    将公共CSS变量设置在父元素中,子元素可以直接使用

    分别设置switch的 on off CSS样式,便于组件调用者
    控制样式风格
*/
.switch {
    margin: 0;
    /* switch元素的宽度,子元素根据该值的固定比例计算实际尺寸 */
    --switch-width: 80px;

    /* off状态的switch背景色 */
    --switch-deactive-color: #fff;

    /* on 状态的 switch背景色 */
    --switch-active-color: #1677fe;

    /* on / off状态的开关滑块背景色 */
    --switch-icon-active-color: #fff;
    --switch-icon-deactive-color: #ccc;

    /* 使用CSS变量 */
    width: var(--switch-width);

    /* 大胆点,你可以使用calc(var(--switch-width) * 0.5) 得到动态计算值 */
    height: calc(var(--switch-width) * 0.5);

    
    background-color: var(--switch-deactive-color);
    border-radius: 20px;
    position: relative;

    /* 别忘了过渡动画 */
    transition: all .5s ease-in-out;
}

/* 子元素可以使用父级元素里的CSS变量 */
.icon {
    margin: 0;
    position: absolute;

    /* 根据switch元素的 CSS变量动态计算*/
    width: calc(var(--switch-width) * 0.5);
    height: calc(var(--switch-width) * 0.5);
    border-radius: 50%;
    background-color: var(--switch-icon-deactive-color);
    left: 0;
    top: 0;

    /* 别忘了开关滑块的过渡效果 */
    transition: all .5s ease-in-out;
}
</style>

3.js部分:

<script lang="ts" setup>
import { ref, onMounted } from 'vue';
//指定onChange回调函数,向外传递switch是否选中
const emit = defineEmits<{ (e: 'onChange', bool: boolean): void }>()

//定义接收的属性并设置默认值
const props = defineProps({
    defaultChecked: {
        type: Boolean,
        default: false
    },
    // 禁用属性默认为false,可用状态
    disable: {
        type: Boolean,
        default: false
    }
})

//组件内switch是否开启的状态
const bool = ref<boolean>(false)

//ref获取元素
const icon = ref<HTMLDivElement | null>(null)
const switchElement = ref<HTMLElement | null>(null)

onMounted(() => {
    // 组件挂载后获取传入的默认是否选中并修改组件的是否选中的状态
    // 将defaultChecked的值和bool值保持一致是不错的选择
    if (bool.value !== props.defaultChecked) bool.value = props.defaultChecked

    // 修改switch对应样式,无论是否禁用都要先根据传入值修改对应样式,
    colorCtrl()
})
// 样式控制器函数
const colorCtrl = () => {
    // 如果禁用状态设置透明度,组件挂载后要执行一下下面对应样式的变化,所以不要return
    if (props.disable) switchElement.value!.style.opacity = '0.8'

    //控制switchElement的背景色
    switchElement.value!.style.backgroundColor = !bool.value ? 'var(--switch-deactive-color)' : 'var(--switch-active-color)';

    // 控制switchElement的边框
    switchElement.value!.style.border = !bool.value ? '1px solid #ccc' : 'none';

    //控制内部开关的位置
    icon.value!.style.left = !bool.value ? '0' : 'calc(var(--switch-width) * 0.5)';

    //控制开关的背景色
    icon.value!.style.backgroundColor = !bool.value ? 'var(--switch-icon-deactive-color)' : 'var(--switch-icon-active-color)';
}

//点击事件的回调函数
const clickHandler = () => {
    //如果switch禁用就不向下进行
    if (props.disable) return

    //修改组件的switch状态
    bool.value = !bool.value

    // 修改样式
    colorCtrl()

    //向外传递boolean
    emit('onChange', bool.value)
}
</script>

4.使用它:

<SwitchComp :default-checked="false" :style="{'--switch-active-color':'pink','--switch-icon-active-color':'purple'}" @on-change="onChange" />

5.禁用状态:

<SwitchComp :default-checked="false" disable :style="{'--switch-active-color':'pink','--switch-icon-active-color':'purple'}" @on-change="onChange" />

6.CSS变量/自定义属性使用的一些点:

  • document.querySelector(':root') === document.docuement //true

  • :root伪类会匹配DOM树的根元素,相对于html文件它代表html*

优先级略高,除此之外与html选择器相同,你可以认为是:root伪类

选择器权重10大于元素选择器1

  • 内联行内选择器的优先级更高,且三者基本关系是内联样式>:root选择器>html选择器

  • 当内联样式或者js设置的值时:document.documentElement.style.getPropertyValue

    获取到的是实际的值

  • 当只有:root选择器或者html选择器设置样式时时,document.documentElement.style.getPropertyValue

    获取到的值是空,因为无法获取元素的内部外部样式表的样式

  • 虽然js无法获取到样式表的CSS变量值,但是不影响document.documentElement.style.setProperty('property',value)*

    因为js设置的CSS样式是行内样式

  • .getComputedStyle(document.documentElement).getPropertyValue(attribute)获取到的始终是

    实际值

  • 四种方式赋值时,如果值包含多个空格,都是总会把多余的空格变成一个

    应该时类似于html页面的元素会将多余空格变成一个空格

真是服了,掘金的编辑器不会用,想搞个图文并茂的没弄明白,复制粘贴自己看吧