动画-主题切换按钮
2024/9/29大约 6 分钟
这个主题切换按钮可以在明/暗模式之间平滑切换,通过 CSS 过渡实现了滑块移动、背景色变化等流畅动画效果,同时支持用户主题偏好记忆功能。
效果展示
<div class="theme-switcher">
<label class="theme-switch">
<input type="checkbox" id="theme-toggle" />
<span class="slider"></span>
</label>
</div>
// 获取主题切换按钮
const themeToggle = document.getElementById("theme-toggle");
// 检查用户偏好或已保存的主题设置
if (
localStorage.theme === "dark" ||
(!("theme" in localStorage) &&
window.matchMedia("(prefers-color-scheme: dark)").matches)
) {
document.documentElement.classList.add("dark");
themeToggle.checked = true;
} else {
document.documentElement.classList.remove("dark");
}
// 主题切换事件处理
themeToggle.addEventListener("change", () => {
if (themeToggle.checked) {
document.documentElement.classList.add("dark");
localStorage.theme = "dark";
} else {
document.documentElement.classList.remove("dark");
localStorage.theme = "light";
}
});
/* 基本容器样式 */
.theme-switcher {
display: flex;
align-items: center;
gap: 10px;
padding: 12px;
}
/* 按钮容器样式 */
.theme-switch {
font-size: 17px;
position: relative;
display: inline-block;
width: 3.5em;
height: 2em;
}
/* 隐藏原生复选框 */
.theme-switch input {
opacity: 0;
width: 0;
height: 0;
}
/* 滑块背景样式 */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #1b1b1f;
transition: background-color 0.5s ease;
border-radius: 30px;
}
/* 滑块圆形部分样式 */
.slider:before {
position: absolute;
content: "";
height: 1.4em;
width: 1.4em;
border-radius: 50%;
left: 10%;
bottom: 15%;
box-shadow: inset 8px -4px 0px 0px #fff000;
background: #1b1b1f;
transition: transform 0.5s ease, box-shadow 0.5s ease, background 0.5s ease;
}
/* 选中状态下的背景颜色 */
input:checked + .slider {
background-color: rgb(200, 200, 200);
}
/* 选中状态下的滑块样式 */
input:checked + .slider:before {
transform: translateX(100%);
box-shadow: inset 15px -4px 0px 15px #fff000;
background: rgb(200, 200, 200);
}
代码解析
HTML 结构
这个动画主题切换按钮的 HTML 结构简洁明了:
- 最外层是一个
.theme-switcher
div,用于包裹整个组件 - 内部使用
<label>
标签包裹<input>
和滑块<span>
<input type="checkbox">
设置了唯一 ID,用于 JavaScript 操作<span class="slider">
用于实现自定义滑块效果
<div class="theme-switcher">
<label class="theme-switch">
<input type="checkbox" id="theme-toggle" />
<span class="slider"></span>
</label>
</div>
这种结构的优点是:
- 使用语义化标签提高可访问性
- 隐藏原生复选框,使用自定义样式提供更好的视觉体验
label
和input
关联,点击标签也能触发状态切换
CSS 动画原理
主题切换按钮的动画效果主要通过 CSS 的 transition
属性实现。下面详细分析 CSS 部分的关键实现:
1. 初始状态
/* 滑块背景样式 */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #1b1b1f;
transition: background-color 0.5s ease;
border-radius: 30px;
}
/* 滑块圆形部分样式 */
.slider:before {
position: absolute;
content: "";
height: 1.4em;
width: 1.4em;
border-radius: 50%;
left: 10%;
bottom: 15%;
box-shadow: inset 8px -4px 0px 0px #fff000;
background: #1b1b1f;
transition: transform 0.5s ease, box-shadow 0.5s ease, background 0.5s ease;
}
初始状态下:
- 滑块背景为深色(
#1b1b1f
) - 圆形滑块位于左侧,通过
box-shadow: inset
创建太阳效果 - 所有可变化属性都设置了
transition
过渡效果,持续时间为 0.5 秒
2. 交互状态
/* 选中状态下的背景颜色 */
input:checked + .slider {
background-color: rgb(200, 200, 200);
}
/* 选中状态下的滑块样式 */
input:checked + .slider:before {
transform: translateX(100%);
box-shadow: inset 15px -4px 0px 15px #fff000;
background: rgb(200, 200, 200);
}
当复选框被选中时(:checked
伪类),通过相邻兄弟选择器(+
)修改滑块样式:
- 滑块背景色变为浅色(
rgb(200, 200, 200)
) - 圆形滑块使用
transform: translateX(100%)
向右移动 - 滑块内部的太阳效果通过调整
box-shadow
变得更大
3. 布局技巧
- 使用相对定位(
.theme-switch
)和绝对定位(.slider
)组合实现精确布局 - 使用
border-radius
创建圆角效果,使界面更柔和 - 通过
cursor: pointer
提供明确的交互反馈 - 使用 Flexbox(
.theme-switcher
)实现组件内部元素的对齐
JavaScript 功能实现
JavaScript 部分实现了主题切换的核心逻辑:
// 获取主题切换按钮
const themeToggle = document.getElementById("theme-toggle");
// 检查用户偏好或已保存的主题设置
if (
localStorage.theme === "dark" ||
(!("theme" in localStorage) &&
window.matchMedia("(prefers-color-scheme: dark)").matches)
) {
document.documentElement.classList.add("dark");
themeToggle.checked = true;
} else {
document.documentElement.classList.remove("dark");
}
// 主题切换事件处理
themeToggle.addEventListener("change", () => {
if (themeToggle.checked) {
document.documentElement.classList.add("dark");
localStorage.theme = "dark";
} else {
document.documentElement.classList.remove("dark");
localStorage.theme = "light";
}
});
JavaScript 实现了以下功能:
初始化主题:页面加载时检查用户偏好和已保存的主题设置
- 检查
localStorage
中是否有保存的主题设置 - 如果没有保存的设置,检查用户系统的颜色方案偏好
- 根据检查结果设置初始主题状态
- 检查
主题切换事件:监听复选框的
change
事件- 当用户切换按钮时,切换
dark
类名 - 将用户的主题偏好保存到
localStorage
中,以便下次访问时保持设置
- 当用户切换按钮时,切换
使用方法
自定义样式
你可以根据需要调整以下样式参数:
- 颜色:修改滑块背景色和太阳效果颜色
/* 自定义暗色主题背景 */
.slider {
background-color: #2d3748;
}
/* 自定义亮色主题背景 */
input:checked + .slider {
background-color: #cbd5e0;
}
/* 自定义太阳颜色 */
.slider:before {
box-shadow: inset 8px -4px 0px 0px #ed8936;
}
- 动画速度:调整过渡动画的持续时间
.slider {
transition: background-color 0.3s ease;
}
.slider:before {
transition: transform 0.3s ease, box-shadow 0.3s ease, background 0.3s ease;
}
- 添加图标:在滑块上添加月亮和太阳图标,增强视觉效果
.slider:before {
/* 原有样式 */
/* 添加太阳图标 */
content: "☀️";
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
}
input:checked + .slider:before {
/* 原有样式 */
/* 切换为月亮图标 */
content: "🌙";
}
完整示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>动画主题切换按钮</title>
<style>
/* 基本容器样式 */
.theme-switcher {
display: flex;
align-items: center;
gap: 10px;
padding: 12px;
}
/* 按钮容器样式 */
.theme-switch {
font-size: 17px;
position: relative;
display: inline-block;
width: 3.5em;
height: 2em;
}
/* 隐藏原生复选框 */
.theme-switch input {
opacity: 0;
width: 0;
height: 0;
}
/* 滑块背景样式 */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #1b1b1f;
transition: background-color 0.5s ease;
border-radius: 30px;
}
/* 滑块圆形部分样式 */
.slider:before {
position: absolute;
content: "";
height: 1.4em;
width: 1.4em;
border-radius: 50%;
left: 10%;
bottom: 15%;
box-shadow: inset 8px -4px 0px 0px #fff000;
background: #1b1b1f;
transition: transform 0.5s ease, box-shadow 0.5s ease,
background 0.5s ease;
}
/* 选中状态下的背景颜色 */
input:checked + .slider {
background-color: rgb(200, 200, 200);
}
/* 选中状态下的滑块样式 */
input:checked + .slider:before {
transform: translateX(100%);
box-shadow: inset 15px -4px 0px 15px #fff000;
background: rgb(200, 200, 200);
}
</style>
</head>
<body>
<div class="theme-switcher">
<label class="theme-switch">
<input type="checkbox" id="theme-toggle" />
<span class="slider"></span>
</label>
</div>
<script>
// 获取主题切换按钮
const themeToggle = document.getElementById("theme-toggle");
// 检查用户偏好或已保存的主题设置
if (
localStorage.theme === "dark" ||
(!"theme" in localStorage &&
window.matchMedia("(prefers-color-scheme: dark)").matches)
) {
document.documentElement.classList.add("dark");
themeToggle.checked = true;
} else {
document.documentElement.classList.remove("dark");
}
// 主题切换事件处理
themeToggle.addEventListener("change", () => {
if (themeToggle.checked) {
document.documentElement.classList.add("dark");
localStorage.theme = "dark";
} else {
document.documentElement.classList.remove("dark");
localStorage.theme = "light";
}
});
</script>
</body>
</html>
浏览器兼容性
相关信息
- 表格中的数字表示该浏览器开始支持的最低版本
- "No" 表示该浏览器不支持此功能
PC 端
浏览器功能 | Chrome | Edge | Firefox | Opera | Safari |
---|---|---|---|---|---|
CSS 变量 | 49 | 15 | 31 | 36 | 10 |
移动端
浏览器功能 | Chrome Android | Firefox Android | Opera Android | Safari iOS | WebView Android |
---|---|---|---|---|---|
CSS 变量 | 49 | 31 | 36 | 10 | 49 |
注意事项
- IE 浏览器兼容性:IE 11 及以下版本不支持 CSS 变量,需要考虑降级方案
- 响应式兼容性:在不同尺寸的设备上,可能需要针对滑动条样式进行微调
更新日志
2025/9/28 09:03
查看所有更新日志
38e56
-于