内部。
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)。
Peachly AI
Peachly AI是一个一体化的AI广告解决方案,帮助企业创建、定位和优化他们的广告活动。
下载
.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