本教程详细讲解如何利用css和隐藏的`input[type="checkbox"]`来构建一个带有动态svg图标的纯css开关组件。通过巧妙运用css选择器和`visibility`属性,我们可以在不依赖javascript的情况下,实现图标根据开关状态自动切换,并提供完整的html和scss代码示例。
在现代Web开发中,创建交互式UI组件是常见的需求。开关(Toggle Switch)作为一种直观的交互元素,常用于控制设置或模式切换。本教程将引导您如何仅使用HTML和SCSS(CSS预处理器)来构建一个功能完善、外观精美的开关组件,其特色在于能够根据开关状态动态切换内部的SVG图标,例如从太阳图标切换到月亮图标,以模拟日夜模式切换。
实现纯CSS交互的关键在于利用HTML的语义化元素和CSS的伪类选择器。input[type="checkbox"] 是一个天然的状态管理器,它有两个状态:选中(checked)和未选中(unchecked)。即使我们将其在视觉上隐藏,其状态仍然可以通过CSS的 :checked 伪类进行检测。通过将这个隐藏的复选框与视觉上的开关组件关联起来,我们可以完全通过CSS来控制组件的外观和内部元素的显示。
开关组件的HTML结构设计旨在提供清晰的语义和灵活的样式控制。一个 元素包裹着隐藏的 和一个包含视觉元素的 。图标(SVG)则嵌套在 内部。 SCSS 样式实现 SCSS 允许我们使用变量、混合(mixins)和嵌套规则,使样式代码更具组织性和可维护性。 1. 变量定义 我们首先定义一系列SCSS变量,用于控制开关的尺寸、颜色和间距,便于统一管理和修改。$width: 80px; $height: 44px; $border-radius: 50px; $circle-size: $height - 4px; // 滑块大小 $icon-size: $circle-size - 2px; // 图标大小 $neutral: red; // 开关背景色 $secondary: white; // 悬停边框色 $base-100: white; // 默认边框色 $base-200: gray; $base-300: black; // 滑块颜色 $base-content: white; // 图标填充色2. 基本布局与外观 .ThemeToggler 是整个开关组件的容器,它定义了开关的整体尺寸、圆角、背景和边框。.ThemeTogglerFill 是内部的填充区域,通过其伪元素 ::before 来创建可滑动的“滑块”(thumb)。.ThemeToggler { width: $width; height: $height; flex-shrink: 0; border-radius: $border-radius; background-color: $neutral; border: 1px solid $base-100; display: inline-block; cursor: pointer; // 鼠标样式 &:hover { border-color: $secondary; // 悬停效果 } } .ThemeTogglerFill { position: relative; // 为内部绝对定位的滑块和图标提供定位上下文 &:before { content: ""; position: absolute; top: 1px; left: 1px; height: $circle-size; width: $circle-size; background: $base-300; box-shadow: 1px 0px 4px rgba(0, 0, 0, 0.15); border-radius: $border-radius; transition: background-color 0.25s, transform 0.25s; // 滑块的动画效果 } }3. 图标样式 我们定义一个 icon 混合(mixin)来封装图标的通用样式,例如尺寸、圆角和填充色。然后,.SunIcon 和 .MoonIcon 分别应用这个 mixin 并设置各自的定位,确保它们在开关内部的正确位置。@mixin icon { position: relative; // 图标容器相对定位,方便内部SVG定位 display: block; width: $icon-size; height: $icon-size; border-radius: 50%; overflow: hidden; // 隐藏溢出部分 fill: $base-content; // SVG的填充色 } .SunIcon { @include icon; top: 1.8px; left: 1.7px; // SVG图标的默认定位 } .MoonIcon { @include icon; top: -35px; // 初始时将月亮图标定位在可见区域之外 left: $circle-size + 2px; }4. 核心:图标的条件显示与隐藏 这是实现动态图标切换的核心部分。我们利用 .ThemeTogglerInput 的 :checked 伪类和相邻兄弟选择器 + 来控制滑块的位移以及太阳/月亮图标的 visibility 属性。.ThemeTogglerInput { display: none; // 隐藏实际的复选框 // 当复选框被选中时,滑块向右移动 &:checked ~ .ThemeTogglerFill::before { transform: translateX($circle-size); } // 默认状态(复选框未选中) // 使用 + 选择器定位到紧邻的兄弟元素 .ThemeTogglerFill & + .ThemeTogglerFill { .MoonIcon { visibility: hidden; // 隐藏月亮图标 } .SunIcon { visibility: visible; // 显示太阳图标 } } // 选中状态(复选框被选中) // 再次使用 + 选择器,但这次是针对 :checked 状态的 input &:checked + .ThemeTogglerFill { .SunIcon { visibility: hidden; // 隐藏太阳图标 } .MoonIcon { visibility: visible; // 显示月亮图标 } } }解释: display: none;:将实际的 input 复选框隐藏起来,使其不占用任何布局空间,但其状态仍然可被CSS访问。 &:checked ~ .ThemeTogglerFill::before:当 input 被选中时,通过通用兄弟选择器 ~ 找到其后的 .ThemeTogglerFill 元素,并作用于其 ::before 伪元素,实现滑块的 transform 动画。 & + .ThemeTogglerFill:这个选择器表示当 input 处于未选中状态时,其紧邻的兄弟元素 .ThemeTogglerFill 内部的 .MoonIcon 应该 visibility: hidden (隐藏),而 .SunIcon 应该 visibility: visible (显示)。 &:checked + .ThemeTogglerFill:当 input 处于选中状态时,其紧邻的兄弟元素 .ThemeTogglerFill 内部的 .SunIcon 应该 visibility: hidden (隐藏),而 .MoonIcon 应该 visibility: visible (显示)。 这里使用 visibility 属性而非 display: none 的原因是,visibility: hidden 会保留元素所占据的布局空间,而 display: none 则会完全移除元素。在本例中,由于图标是绝对定位的,两者在视觉效果上可能差异不大,但 visibility 可以为将来的过渡效果提供更多可能性(尽管 visibility 属性本身通常不能平滑过渡)。 完整的 SCSS 代码 将上述所有 SCSS 片段整合,形成完整的样式代码: $width: 80px; $height: 44px; $border-radius: 50px; $circle-size: $height - 4px; $icon-size: $circle-size - 2px; $neutral: red; $secondary: white; $base-100: white; $base-200: gray; $base-300: black; $base-content: white; .ThemeToggler { width: $width; height: $height; flex-shrink: 0; border-radius: $border-radius; background-color: $neutral; border: 1px solid $base-100; display: inline-block; cursor: pointer; &:hover { border-color: $secondary; } } .ThemeTogglerFill { position: relative; &:before { content: ""; position: absolute; top: 1px; left: 1px; height: $circle-size; width: $circle-size; background: $base-300; box-shadow: 1px 0px 4px rgba(0, 0, 0, 0.15); border-radius: $border-radius; transition: background-color 0.25s, transform
SCSS 允许我们使用变量、混合(mixins)和嵌套规则,使样式代码更具组织性和可维护性。
我们首先定义一系列SCSS变量,用于控制开关的尺寸、颜色和间距,便于统一管理和修改。
$width: 80px; $height: 44px; $border-radius: 50px; $circle-size: $height - 4px; // 滑块大小 $icon-size: $circle-size - 2px; // 图标大小 $neutral: red; // 开关背景色 $secondary: white; // 悬停边框色 $base-100: white; // 默认边框色 $base-200: gray; $base-300: black; // 滑块颜色 $base-content: white; // 图标填充色
.ThemeToggler 是整个开关组件的容器,它定义了开关的整体尺寸、圆角、背景和边框。.ThemeTogglerFill 是内部的填充区域,通过其伪元素 ::before 来创建可滑动的“滑块”(thumb)。
.ThemeToggler { width: $width; height: $height; flex-shrink: 0; border-radius: $border-radius; background-color: $neutral; border: 1px solid $base-100; display: inline-block; cursor: pointer; // 鼠标样式 &:hover { border-color: $secondary; // 悬停效果 } } .ThemeTogglerFill { position: relative; // 为内部绝对定位的滑块和图标提供定位上下文 &:before { content: ""; position: absolute; top: 1px; left: 1px; height: $circle-size; width: $circle-size; background: $base-300; box-shadow: 1px 0px 4px rgba(0, 0, 0, 0.15); border-radius: $border-radius; transition: background-color 0.25s, transform 0.25s; // 滑块的动画效果 } }
我们定义一个 icon 混合(mixin)来封装图标的通用样式,例如尺寸、圆角和填充色。然后,.SunIcon 和 .MoonIcon 分别应用这个 mixin 并设置各自的定位,确保它们在开关内部的正确位置。
@mixin icon { position: relative; // 图标容器相对定位,方便内部SVG定位 display: block; width: $icon-size; height: $icon-size; border-radius: 50%; overflow: hidden; // 隐藏溢出部分 fill: $base-content; // SVG的填充色 } .SunIcon { @include icon; top: 1.8px; left: 1.7px; // SVG图标的默认定位 } .MoonIcon { @include icon; top: -35px; // 初始时将月亮图标定位在可见区域之外 left: $circle-size + 2px; }
这是实现动态图标切换的核心部分。我们利用 .ThemeTogglerInput 的 :checked 伪类和相邻兄弟选择器 + 来控制滑块的位移以及太阳/月亮图标的 visibility 属性。
.ThemeTogglerInput { display: none; // 隐藏实际的复选框 // 当复选框被选中时,滑块向右移动 &:checked ~ .ThemeTogglerFill::before { transform: translateX($circle-size); } // 默认状态(复选框未选中) // 使用 + 选择器定位到紧邻的兄弟元素 .ThemeTogglerFill & + .ThemeTogglerFill { .MoonIcon { visibility: hidden; // 隐藏月亮图标 } .SunIcon { visibility: visible; // 显示太阳图标 } } // 选中状态(复选框被选中) // 再次使用 + 选择器,但这次是针对 :checked 状态的 input &:checked + .ThemeTogglerFill { .SunIcon { visibility: hidden; // 隐藏太阳图标 } .MoonIcon { visibility: visible; // 显示月亮图标 } } }
解释:
这里使用 visibility 属性而非 display: none 的原因是,visibility: hidden 会保留元素所占据的布局空间,而 display: none 则会完全移除元素。在本例中,由于图标是绝对定位的,两者在视觉效果上可能差异不大,但 visibility 可以为将来的过渡效果提供更多可能性(尽管 visibility 属性本身通常不能平滑过渡)。
将上述所有 SCSS 片段整合,形成完整的样式代码:
$width: 80px; $height: 44px; $border-radius: 50px; $circle-size: $height - 4px; $icon-size: $circle-size - 2px; $neutral: red; $secondary: white; $base-100: white; $base-200: gray; $base-300: black; $base-content: white; .ThemeToggler { width: $width; height: $height; flex-shrink: 0; border-radius: $border-radius; background-color: $neutral; border: 1px solid $base-100; display: inline-block; cursor: pointer; &:hover { border-color: $secondary; } } .ThemeTogglerFill { position: relative; &:before { content: ""; position: absolute; top: 1px; left: 1px; height: $circle-size; width: $circle-size; background: $base-300; box-shadow: 1px 0px 4px rgba(0, 0, 0, 0.15); border-radius: $border-radius; transition: background-color 0.25s, transform
# css # javascript # java # html # svg # 伪元素 # 处理器 # switch # css选择器 # 绝对定位
相关栏目: 【 技术学院 】 【 SEO学院 】 【 AI学院 】 【 编程学院 】 【 推广学院 】
相关推荐: Laravel怎么在生产环境中关闭debug模式及影响 How to Properly Use NumPy in VS Code Laravel Pint怎么格式化代码_使用Laravel Pint实现PHP代码风格自动化 本地php环境打开php文件直接下载_浏览器解析php为下载的修复方法【解答】 Laravel如何处理文件上传_Laravel Storage门面实现文件存储与管理 C++中的constexpr和const有什么区别?(编译期常量) Laravel中DTO是什么概念_在Laravel项目中使用数据传输对象(DTO) Laravel如何创建和使用自定义的Blade组件 Laravel Octane如何提升性能_使用Laravel Octane加速你的应用 Laravel控制器如何创建_Laravel资源控制器使用详解 Laravel如何使用Seeder填充数据_Laravel模型工厂Factory批量生成测试数据【方法】 如何高效识别并拦截拼接式恶意域名 spam Laravel怎么使用artisan命令缓存配置和视图 c++ unordered_map怎么用 c++哈希表用法【教程】 Laravel如何为应用配置HTTPS/SSL Python正则表达式实战_模式匹配说明【教程】 如何使用Golang encoding/json解析JSON_Golang encoding/json解析与序列化示例 如何在Golang中修改字符串内容_通过指针实现高效操作 Laravel composer install慢怎么办_Laravel配置Composer国内镜像 Laravel怎么进行数据库回滚_Laravel Migration数据库版本控制与回滚操作 C++如何解析JSON数据?(nlohmann/json库示例) 如何使用Golang处理静态文件缓存_提高页面加载速度 Laravel广播系统如何实现实时通信_Laravel Reverb与WebSockets实战教程 php怎么下载安装后无法解析php文件_服务器配置检查【解答】 Laravel怎么使用Collection集合方法_Laravel数组操作高级函数pluck与map【手册】 php485读数据时阻塞怎么办_php485非阻塞读取设置技巧【详解】 VSC怎样用终端运行PHP_命令行执行脚本的步骤【教程】 Laravel如何自定义用户认证(Authentication)流程 Laravel如何配置中间件Middleware_Laravel自定义中间件拦截请求与权限校验【步骤】 使用类变量定义字符串常量时如何实现类型安全的 Literal 注解 laravel服务容器和依赖注入怎么理解_laravel服务容器与依赖注入解析 php订单日志怎么在swoole写_php协程swoole写订单日志教程【教程】 如何提升Golang程序I/O性能_Golang I/O密集型程序优化示例 Laravel如何实现API版本控制_Laravel API版本化路由设计策略 如何使用Golang实现云原生应用弹性伸缩_自动应对流量变化 如何使用Golang benchmark测量函数延迟_统计执行耗时 laravel怎么实现数据库读写分离_laravel数据库读写分离实现方法 c++怎么操作redis数据库_c++ hiredis库连接与命令执行【实战】 php本地部署支持nodejs吗_php与nodejs混合开发环境搭建教程【教程】 php删除数据怎么软删除_添加is_del字段标记删除【技巧】 Laravel怎么在Blade中安全地输出原始HTML内容 Laravel如何使用withoutEvents方法临时禁用模型事件 php怎么下载安装后设置默认字符集_utf8配置步骤【详解】 c# 如何用c#实现一个支持优先级的任务队列 Laravel如何与Pusher实现实时通信?(WebSocket示例) Laravel如何广播事件实现实时通信_Laravel广播系统与实时数据推送 如何高效识别两个DataFrame中指定列值不同的行(基于键列匹配) Flask 表单数据通过 SMTP 发送邮件的完整实现教程 短链接怎么自定义还原php_修改解码规则适配需求【汇总】 Python字符串处理进阶_切片方法解析【指导】
首页
关于我们
+
产品展示
新闻中心
户外常识
在线留言
联系我们
搜索