当前位置: 首页 > news >正文

三开关VUE组件

一、使用效果

请添加图片描述请添加图片描述

请添加图片描述

<template><QqThreeSwitch v-model="value" /><!-- <SqThreeSwitch v-model="value" :options="['test1', 'test2', 'test3']"><template #left-action><div style="display: flex"><IconMoon /></div></template><template #middle-action><div style="display: flex"><IconSunny /></div></template><template #right-action><div style="display: flex"><IconSystem /></div></template></SqThreeSwitch> -->
</template><script setup>
import SqThreeSwitch from './components/SqThreeSwitch.vue'
import { ref } from 'vue'const value = ref(0)
</script>

二、SqThreeSwitch.vue源码

<template><div class="sq-three-switch"><button class="focus-btn" :style="focusBtnStyle" @click="handleBtnClick">按下空格切换主题, 当前选择:{{ selectedOption }}</button><div v-show="isMouseEnter" class="tooltip" tabindex="-1" :style="tooltipStyle"><div class="tip-text">{{ tooltipText }}</div><svg class="tip-arrow" width="16px" height="8px" :style="tipArrowStyle"><polygon points="0,-1 8,7 16,-1" /></svg></div><div ref="selectedOptionRef" class="selected-option"><span>{{ selectedOption }}</span></div><divref="controlRef"class="control plane-border"@click="handleClick"@mouseenter="handleMouseEnter"@mouseleave="handleMouseLeave"@mousemove="debouncedHandleMouseMove"></div><div class="plane"></div><div class="badge-dots"><divv-for="(dot, index) in [0, 1, 2]":key="index"class="dot":class="{ 'dot-animate': dotAnimateFlag }"@animationend="handleAnimationEnd"></div></div><div class="handle" :style="handleStyle"><slot v-if="modelValue === 0" name="left-action"></slot><slot v-if="modelValue === 1" name="middle-action"></slot><slot v-if="modelValue === 2" name="right-action"></slot></div></div>
</template><script setup>
import { ref, watch, computed, nextTick, onMounted, onBeforeUnmount } from 'vue'
import { useDebounceFn } from '@vueuse/core'const props = defineProps({modelValue: {type: Number,default: 0},options: {type: Array,default: () => ['选项A', '选项B', '选项C']}
})
const emit = defineEmits(['update:modelValue'])const selectedOptionRef = ref(null)const focusBtnStyle = ref({})
nextTick(() => {focusBtnStyle.value = {width: `${selectedOptionRef.value.getBoundingClientRect().width + 50}px`}
})
watch(() => props.modelValue,() => {nextTick(() => {focusBtnStyle.value = {width: `${selectedOptionRef.value.getBoundingClientRect().width + 50}px`}})}
)const controlRef = ref(null)
const haveTooltipSpace = ref(false)
const tipArrowStyle = computed(() => {return {transform: haveTooltipSpace.value ? '' : 'translateY(-26px) rotate(180deg)'}
})
function checkTooltipSpace(deadline) {if (deadline.timeRemaining() > 0) {const rect = controlRef.value?.getBoundingClientRect()if (rect) {haveTooltipSpace.value = rect.top >= 20}}
}
const debouncedCheckTooltipSpace = useDebounceFn(() => requestIdleCallback(checkTooltipSpace, { timeout: 200 }),200
)
let intervalId
onMounted(() => {debouncedCheckTooltipSpace()window.addEventListener('scroll', debouncedCheckTooltipSpace)window.addEventListener('resize', debouncedCheckTooltipSpace)intervalId = setInterval(debouncedCheckTooltipSpace, 2000)console.log('作者主页: https://blog.csdn.net/qq_39124701')
})
onBeforeUnmount(() => {window.removeEventListener('scroll', debouncedCheckTooltipSpace)window.removeEventListener('resize', debouncedCheckTooltipSpace)if (intervalId !== null) {clearInterval(intervalId)}
})const isMouseEnter = ref(false)
const tooltipText = ref(props.options[props.modelValue])
const tooltipStyle = ref({left: props.modelValue === 0 ? '0px' : props.modelValue === 1 ? '20px' : '40px',top: haveTooltipSpace.value ? '0px' : '54px'
})const selectedOption = computed(() => {return props.options[props.modelValue]
})const dotAnimateFlag = ref(false)const handleStyle = ref({left:props.modelValue === 0? '2px': props.modelValue === 1? 'calc(50% - 9px)': 'calc(100% - 19px)'
})
watch(() => props.modelValue,(newValue) => {handleStyle.value = {left: newValue === 0 ? '2px' : newValue === 1 ? 'calc(50% - 9px)' : 'calc(100% - 19px)'}}
)function handleClick(/** @type { MouseEvent } */ event) {/** @type { Element } */const eventTarget = event.targetconst rect = eventTarget.getBoundingClientRect()const clickX = event.clientX - rect.leftconst oneThirdWidth = rect.width / 3if (clickX < oneThirdWidth) {if (props.modelValue === 0) {dotAnimateFlag.value = true}emit('update:modelValue', 0)} else if (clickX > oneThirdWidth * 2) {if (props.modelValue === 2) {dotAnimateFlag.value = true}emit('update:modelValue', 2)} else {if (props.modelValue === 1) {dotAnimateFlag.value = true}emit('update:modelValue', 1)}
}
function handleBtnClick() {if (props.modelValue === 0) {emit('update:modelValue', 1)} else if (props.modelValue === 1) {emit('update:modelValue', 2)} else if (props.modelValue === 2) {emit('update:modelValue', 0)}
}
function handleMouseEnter() {isMouseEnter.value = true
}
function handleMouseLeave() {isMouseEnter.value = false
}
const debouncedHandleMouseMove = useDebounceFn(handleMouseMove, 40)
function handleMouseMove(event) {if (!isMouseEnter.value) {return}const rect = event.target.getBoundingClientRect()const clickX = event.clientX - rect.leftconst oneThirdWidth = rect.width / 3if (clickX < oneThirdWidth) {tooltipText.value = props.options[0]tooltipStyle.value = { left: '0px', top: haveTooltipSpace.value ? '0px' : '54px' }} else if (clickX > oneThirdWidth * 2) {tooltipText.value = props.options[2]tooltipStyle.value = { left: 'calc(100% - 21px)', top: haveTooltipSpace.value ? '0px' : '54px' }} else {tooltipText.value = props.options[1]tooltipStyle.value = { left: 'calc(50% - 11px)', top: haveTooltipSpace.value ? '0px' : '54px' }}
}function handleAnimationEnd() {dotAnimateFlag.value = false
}
</script><style scoped>
.sq-three-switch {position: relative;width: 60px;height: 20px;/* scale: 5;transform-origin: 0% 0%;z-index: 1; */
}
.sq-three-switch > * {position: absolute;
}
.sq-three-switch > .plane,
.sq-three-switch > .badge-dots,
.sq-three-switch > .handle {pointer-events: none;
}
.sq-three-switch > .focus-btn {height: 100%;border-radius: 10px;border: 0;outline-offset: 1px;font-size: 0;
}
.sq-three-switch > .focus-btn:focus {outline: 2px solid #409eff;
}
.sq-three-switch > .tooltip {z-index: 1;transform: translateY(-27px);white-space: nowrap;background-color: #e6e6e6;border: 1px solid gray;border-radius: 4px;padding: 1px 11px;transition: left 0.2s;
}
.sq-three-switch > .tooltip > .tip-text {font-size: 12px;color: black;
}
.sq-three-switch > .tooltip > .tip-arrow {position: absolute;top: 18px;left: 1px;
}
.sq-three-switch > .tooltip > .tip-arrow polygon {fill: #e6e6e6;stroke: gray;stroke-width: 1;
}
.sq-three-switch > .selected-option {height: 100%;background: linear-gradient(to right, #a8d4ff, #409eff 16px);border-radius: 10px;border-top-left-radius: 0;border-bottom-left-radius: 0;transform: translateX(50px);display: flex;justify-content: center;font-size: 14px;color: white;white-space: nowrap;
}
.sq-three-switch > .selected-option > span {padding-left: 16px;padding-right: 10px;user-select: none;
}
.sq-three-switch > .control {width: 100%;height: 20px;border-radius: 10px;background: #409eff;cursor: pointer;
}
.sq-three-switch > .plane {top: 1px;left: 1px;width: calc(100% - 2px);height: 18px;border-radius: 10px;background: #409eff;
}
.sq-three-switch > .badge-dots > .dot {position: absolute;top: 8px;left: 8px;width: 4px;height: 4px;border-radius: 100%;transition: all 0.3s cubic-bezier(0.22, 0.61, 0.36, 1);background-color: white;
}
.sq-three-switch > .badge-dots > .dot:nth-child(2) {left: 27px;
}
.sq-three-switch > .badge-dots > .dot:nth-child(3) {left: 47px;
}
.dot-animate {animation: dotAnimation 0.3s;
}
@keyframes dotAnimation {0% {background-color: white;}25% {background-color: black;}50% {background-color: white;}75% {background-color: black;}100% {background-color: white;}
}
.sq-three-switch > .handle {top: 2px;left: 2px;width: 16px;height: 16px;border-radius: 100%;transition: all 0.3s cubic-bezier(0.22, 0.61, 0.36, 1);background-color: white;
}html.dark .sq-three-switch > .tooltip {background-color: #303133;
}
html.dark .sq-three-switch > .tooltip > .tip-arrow polygon {fill: #303133;
}
html.dark .sq-three-switch > .tooltip > .tip-text {color: white;
}
</style>

相关文章:

三开关VUE组件

一、使用效果 <template><QqThreeSwitch v-model"value" /><!-- <SqThreeSwitch v-model"value" :options"[test1, test2, test3]"><template #left-action><div style"display: flex"><IconMoon…...

SpringCloud+SpringCloudAlibaba学习笔记

SpringCloud 服务注册中心 eureka ap 高可用 分布式容错 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency><groupId…...

牛客小白月赛105(A~E)

文章目录 A lz的吃饭问题思路code B lz的数字问题思路code C lz的蛋挞问题思路code D lz的染色问题思路code E lz的括号问题思路code 总结 牛客小白月赛105 A lz的吃饭问题 思路 签到题&#xff0c;比较大小即可 code void solve(){int a,b,c,d;cin >> a >> b…...

OSPF协议整理

OSPF&#xff08;Open Shortest Path First&#xff09;即开放式最短路径优先协议&#xff0c;是一种广泛应用于大型网络中的链路状态路由协议。 OSPF 的基本概念 OSPF 是基于链路状态算法的内部网关协议&#xff08;IGP&#xff09;&#xff0c;用于在一个自治系统&#xff…...

Java中的多线程

文章目录 Java中的多线程一、引言二、多线程的创建和启动1、继承Thread类2、实现Runnable接口 三、线程的常用方法1、currentThread()和getName()2、sleep()和yield()3、join() 四、线程优先级五、使用示例六、总结 Java中的多线程 一、引言 在Java中&#xff0c;多线程编程是…...

什么是聚簇索引、非聚簇索引、回表查询

其实聚集索引也叫聚簇索引&#xff0c;二级索引也叫非聚簇索引&#xff0c;大家不要认为这是不同的两个知识点。 定义 先看一下数据库的索引介绍。 聚簇索引 1. 如果存在主键&#xff08;一般都存在&#xff09;&#xff0c;主键索引就是聚簇索引。 2. 如果不存在&#xff0c;…...

探索 Spring 框架核心组件:构建强大 Java 应用的基石

Spring框架作为Java企业级开发的首选框架之一&#xff0c;其强大的功能和灵活的架构深受开发者喜爱。Spring框架的核心组件共同构建了一个高效、可扩展的应用程序开发平台。本文将深入探讨Spring框架的核心组件&#xff0c;揭示它们如何在Spring框架中发挥关键作用。 一、Bean…...

Android 13 Aosp 默认允许应用动态权限

图库 frameworks/base/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java 修改 public void grantDefaultPermissions(int userId) {DelayingPackageManagerCache pm new DelayingPackageManagerCache();grantPermissionsToSysCompon…...

【C++知识总结1】c++第一篇,简单了解一下命名空间是什么

一、C的由来 C语言是一种结构化和模块化的编程语言&#xff0c;它对于处理较小规模的程序非常适用。然而&#xff0c;当面临需要高度抽象和建模的复杂问题&#xff0c;以及规模较大的程序时&#xff0c;C语言就显得不那么合适了。为了应对这种挑战&#xff0c;并在解决软件危机…...

从0开始深度学习(32)——循环神经网络的从零开始实现

本章将从零开始&#xff0c;基于循环神经网络实现字符级语言模型&#xff08;不是单词级&#xff09; 首先我们把从0开始深度学习&#xff08;30&#xff09;——语言模型和数据集中的load_corpus_time_machine()函数进行引用&#xff0c;用于导入数据&#xff1a; train_iter…...

GitLab使用操作v1.0

1.前置条件 Gitlab 项目地址&#xff1a;http://******/req Gitlab账户信息&#xff1a;例如 001/******自己的分支名称&#xff1a;例如 001-master&#xff08;注&#xff1a;master只有项目创建者有权限更新&#xff0c;我们只能更新自己分支&#xff0c;然后创建合并请求&…...

cuda conda yolov11 环境搭建

优雅的 yolo v11 标注工具 AutoLabel Conda环境直接识别训练 nvidia-smi 检查CUDA版本 下载nvidia cudnn对应的版本 将cuDNN压缩包内对应的文件复制到本地bin、include、lib的文件夹中 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.6 miniConda快速开始-安装 执行…...

解决SpringBoot连接Websocket报:请求路径 404 No static resource websocket.

问题发现 最近在工作中用到了WebSocket进行前后端的消息通信&#xff0c;后端代码编写完后&#xff0c;测试一下是否连接成功&#xff0c;发现报No static resource websocket.&#xff0c;看这个错貌似将接口变成了静态资源来访问了&#xff0c;第一时间觉得是端点没有注册成…...

element-plus的组件数据配置化封装 - table

目录 一、封装的table、table-column组件以及相关ts类型的定义 1、ATable组件的封装 - index.ts 2、ATableColumn组件的封装 - ATableColumn.ts 3、ATable、ATableColumn类型 - interface.ts 二、ATable、ATableColumn组件的使用 三、相关属性、方法的使用以及相关说明 1. C…...

【二维动态规划:交错字符串】

介绍 编程语言&#xff1a;Java 本篇介绍一道比较经典的二维动态规划题。 交错字符串 主要说明几点&#xff1a; 为什么双指针解不了&#xff1f;为什么是二维动态规划&#xff1f;根据题意分析处转移方程。严格位置依赖和空间压缩优化。 题目介绍 题意有点抽象&#xff0c…...

goframe开发一个企业网站 MongoDB 完整工具包18

1. MongoDB 工具包完整实现 (mongodb.go) package mongodbimport ("context""fmt""time""github.com/gogf/gf/v2/frame/g""go.mongodb.org/mongo-driver/mongo""go.mongodb.org/mongo-driver/mongo/options" )va…...

在vue中,根据后端接口返回的文件流实现word文件弹窗预览

需求 弹窗预览word文件&#xff0c;因浏览器无法直接根据blob路径直接预览word文件&#xff0c;所以需要利用插件实现。 解决方案 利用docx-preview实现word文件弹窗预览&#xff0c;以node版本16.21.3和docx-preview版本0.1.8为例 具体实现步骤 1、安装docx-preview插件 …...

动态规划之背包问题

0/1背包问题 1.二维数组解法 题目描述&#xff1a;有一个容量为m的背包&#xff0c;还有n个物品&#xff0c;他们的重量分别为w1、w2、w3.....wn&#xff0c;他们的价值分别为v1、v2、v3......vn。每个物品只能使用一次&#xff0c;求可以放进背包物品的最大价值。 输入样例…...

【Python】 深入理解Python的单元测试:用unittest和pytest进行测试驱动开发

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 单元测试是现代软件开发中的重要组成部分,通过验证代码的功能性、准确性和稳定性,提升代码质量和开发效率。本文章深入介绍Python中两种主流单元测试框架:unittest和pytest,并结合测试驱动开发(TDD)…...

Java集合1.0

1.什么是集合&#xff1f; 集合就是一个存放数据的容器&#xff0c;准确的说是放数据对象引用的容器。 集合和数组的区别 数组是固定长度&#xff0c;集合是可变长度。数组可以存储基本数据类型&#xff0c;也可以存储引用数据类型&#xff0c;集合只能存储引用数据类型&…...

Leetcode 336 回文对

示例 1&#xff1a; 输入&#xff1a;words ["abcd","dcba","lls","s","sssll"] 输出&#xff1a;[[0,1],[1,0],[3,2],[2,4]] 解释&#xff1a;可拼接成的回文串为 ["dcbaabcd","abcddcba","sl…...

实现一个可配置的TCP设备模拟器,支持交互和解析配置

前言 诸位在做IOT开发的时候是否有遇到一个问题&#xff0c;那就是模拟一个设备来联调测试&#xff0c;虽然说现在的物联网通信主要是用mqtt通信&#xff0c;但还是有很多设备使用TCP这种协议交互&#xff0c;例如充电桩&#xff0c;还有一些工业设备&#xff0c;TCP这类报文交…...

算法的空间复杂度

空间复杂度 空间复杂度主要是衡量一个算法运行所需要的额外空间&#xff0c;在计算机发展早期&#xff0c;计算机的储存容量很小&#xff0c;所以空间复杂度是很重要的。但是经过计算机行业的迅速发展&#xff0c;计算机的容量已经不再是问题了&#xff0c;所以如今已经不再需…...

自定义协议

1. 问题引入 问题&#xff1a;TCP是面向字节流的&#xff08;TCP不关心发送的数据是消息、文件还是其他任何类型的数据。它简单地将所有数据视为一个字节序列&#xff0c;即字节流。这意味着TCP不会对发送的数据进行任何特定的边界划分&#xff0c;它只是确保数据的顺序和完整…...

在 Taro 中实现系统主题适配:亮/暗模式

目录 背景实现方案方案一&#xff1a;CSS 变量 prefers-color-scheme 媒体查询什么是 prefers-color-scheme&#xff1f;代码示例 方案二&#xff1a;通过 JavaScript 监听系统主题切换 背景 用Taro开发的微信小程序&#xff0c;需求是页面的UI主题想要跟随手机系统的主题适配…...

autogen框架中使用chatglm4模型实现react

本文将介绍如何使用使用chatglm4实现react&#xff0c;利用环境变量、Tavily API和ReAct代理模式来回答用户提出的问题。 环境变量 首先&#xff0c;我们需要加载环境变量。这可以通过使用dotenv库来实现。 from dotenv import load_dotenv_ load_dotenv()注意.env文件处于…...

读《Effective Java》笔记 - 条目9

条目9&#xff1a;与try-finally 相比&#xff0c;首选 try -with -resource 什么是 try-finally&#xff1f; try-finally 是 Java 中传统的资源管理方式&#xff0c;通常用于确保资源&#xff08;如文件流、数据库连接等&#xff09;被正确关闭。 BufferedReader reader n…...

【软件入门】Git快速入门

Git快速入门 文章目录 Git快速入门0.前言1.安装和配置2.新建版本库2.1.本地创建2.2.云端下载 3.版本管理3.1.添加和提交文件3.2.回退版本3.2.1.soft模式3.2.2.mixed模式3.2.3.hard模式3.2.4.使用场景 3.3.查看版本差异3.4.忽略文件 4.云端配置4.1.Github4.1.1.SSH配置4.1.2.关联…...

nextjs window is not defined

问题产生的原因 在 Next.js 中&#xff0c;“window is not defined” 错误通常出现在服务器端渲染&#xff08;Server - Side Rendering&#xff0c;SSR&#xff09;的代码中。这是因为window对象是浏览器环境中的全局对象&#xff0c;在服务器端没有window这个概念。例如&am…...

C语言实现冒泡排序:从基础到优化全解析

一、什么是冒泡排序&#xff1f; 冒泡排序&#xff08;Bubble Sort&#xff09;是一种经典的排序算法&#xff0c;其工作原理非常直观&#xff1a;通过多次比较和交换相邻元素&#xff0c;将较大的元素“冒泡”到数组的末尾。经过多轮迭代&#xff0c;整个数组会变得有序。 二…...

矢量插画的网站/网站建设问一问公司

前言 对于很多Python这门编程语言的初学者&#xff0c;往往会面临以下问题&#xff1a; 是否要安装Linux系统学习Python&#xff1f; Python3有各种版本我该安装哪一个&#xff1f; 那么多的图书、视频和电子教程我该选择哪一个&#xff1f; 各种开发工具我该使用哪一个&am…...

设计做兼职最好的网站/各网站收录

状态模式又是一个比较难的设计模式 定义如下&#xff1a; 当一个对象内在状态改变时允许其改变行为&#xff0c;这个对象看起来像改变了其类。 个人理解&#xff1a;通俗的讲&#xff0c;状态模式就是状态的改变引起了行为的改变&#xff0c;但是&#xff0c;我们只能看到行为的…...

jetpack wordpress/腾讯企点官网

具体请看这里→http://www.k-zone.cn/zblog/post/log-2006-11-21-001.html 呵呵&#xff0c;难得大家对这篇文章感兴趣。今天我就跟大家讨论一下FlexCSharp这种架构具体的实现办法。1、 开发环境准备。在上一篇我已经说明了&#xff0c;开发环境是vs2005Flex2&#xff0c;也就是…...

word文档做网站/营业推广

ES6为Array增加了find()&#xff0c;findIndex函数。 find()函数用来查找目标元素&#xff0c;找到就返回该元素&#xff0c;找不到返回undefined。 findIndex()函数也是查找目标元素&#xff0c;找到就返回元素的位置&#xff0c;找不到就返回-1。 他们的都是一个查找回调函数…...

网络营销类型有哪些/seo推广策略

#define UsartRxBuffAddressArmEntry 0X20001000 #define UsartRxBuffSize 0x400 unsigned char USART_RX_BUF[UsartRxBuffSize ] __ attribute __((at(UsartBuffAddressArmEntry ))); //将串口接收的数据定位存放到RAM中地址为0X20001000–0x200013FF 中&#xff1b; #defin…...

wordpress手动更新插件/百度推广的方式

我有点困惑&#xff0c;我无法使用MSVC在Windows上进行C开发设置Visual Studio Code . 网络上的所有人都说他们对设置和使用一切都很容易感到高兴&#xff0c;但我找不到任何简单的指南;他们中的大多数只是跳过设置部分&#xff0c;并显示一切有效&#xff0c;包括代码完成/智能…...