效果图
思路:
1. 高亮的色块是独立的一个盒子,需要插入当前激活的内容用来撑开色块盒子的宽度,这样色块的宽度就会和当前激活的内容宽度一致,色块的字体颜色设置透明即可
2. 色块滑动的距离是读当前激活元素的offsetLeft,赋值给色块盒子的translateX 属性
3. 使用vue3的新属性,在css中使用v-bind()动态的设置可变化的属性
4. 在色块盒子加上过渡的属性transition,即可实现色块的滑动过度效果
实现代码
// 新建CSlideSwitch.vue组件
<template><div class="c-slide-switch"><div class="container"><!-- silder 是高亮的颜色 --><span class="slider" :class="{ 'is-transition': animation }">{{ showSliderName }}</span><spanv-for="(item, index) in dataSource":key="index"ref="sliderItemRef"style="z-index: 66":class="{ actived: currentValue === item[propsAttr.value] }"@click="changeSlide(index, item[propsAttr.value])">{{ item[propsAttr.label] }}</span></div></div>
</template>
<script setup lang="ts">
/*** 这是 滑动切换组件*/
import { ref, computed, watch } from "vue";
type Props = {modelValue?: any; // 数值dataSource: any[]; // 数据源propsObj?: { [key: string]: any }; // 读取的字段属性animation?: boolean; // 是否开启动画duration?: number; // 动画时长 注意单位为毫秒
};
const props = withDefaults(defineProps<Props>(), {modelValue: null,dataSource: () => [],propsObj: () => {// 属性return {};},animation: true,duration: 500
});
const emit = defineEmits(["update:modelValue", "change"]);const propsAttr = computed(() => {const obj = {label: "label",value: "value"};return Object.assign(obj, props.propsObj);
});const sliderItemRef = ref(); // slider下的每个item实例
const currentValue = ref(props.modelValue); // 记录当前激活的值
const sliderOffsetLeft = ref("0"); // 记录滑块需要滑动的距离// 用于在slider滑块上展示的文案--这个文案的作用主要是撑开slider滑块的宽度
const showSliderName = computed(() => {const target = props.dataSource.find((item: any) => item[propsAttr.value.value] === currentValue.value);return target[propsAttr.value.label];
});
// 滑块的动画时常
const sliderDuration = computed(() => {return (props.duration / 1000) + "s";
})// 监听激活的值的变化发射事件
watch(() => currentValue.value,() => {emit("update:modelValue", currentValue.value);},{ immediate: true }
);// 切换滑块
const changeSlide = (index: number, value: any) => {const offset = 2; // 偏移量// 更改滑块 滑动的距离sliderOffsetLeft.value = sliderItemRef.value[index].offsetLeft - offset + "px";// 记录当前激活的值currentValue.value = value;emit("change", value);
};
</script><style scoped lang="scss">
.container {position: relative;display: inline-flex;align-items: center;padding: 3px;overflow: hidden;background: rgba($color: #000000, $alpha: 10%);border-radius: 20px;span {display: inline-block;padding: 2px 24px;font-size: 12px;color: ##606266;cursor: pointer;}.slider {position: absolute;display: inline-block;transform: translateX(v-bind(sliderOffsetLeft));overflow: hidden;color: transparent;background-color: ##409EFF;border-radius: 20px;}.is-transition {transition: all v-bind(sliderDuration);}.actived {z-index: 99;font-weight: 600;color: #ffffff;border-radius: 20px;}
}
</style>
在文件中使用滑动切换组件
<template><div><h2>滑动切换组件</h2><CSlideSwitch v-model="slideValue" :data-source="btnList" /><div>绑定的值:{{ slideValue }}</div></div>
</template><script setup lang="ts">
import { ref } from "vue";
import CSlideSwitch from "@/components/modules/CSlideSwitch/index.vue";const btnList = [{ label: "今天", value: "today" },{ label: "昨天", value: "yesterday" },{ label: "本周", value: "this_week" },{ label: "上周", value: "last_week" },{ label: "本月", value: "this_month" },{ label: "上月", value: "last_month" }
];const slideValue = ref("today");
</script>