圆点位移动画本质是transform: translateX()的平滑过渡,需显式声明transition: transform 0.3s cubic-bezier(0.4,0,0.2,1),配合:checked~.slider选择器与固定尺寸布局实现。

用 transition 控制圆点位移动画
开关按钮的“圆点滚动”本质是 transform: translateX() 的平滑变化,不是靠 JS 一帧帧算位置。关键在给圆点元素加 transition,且只作用于 transform 属性,避免触发布局重排。
常见错误是写成 transition: all 0.3s —— 这会让颜色、边框甚至宽高变化也参与过渡,拖慢性能,还可能在 Safari 上触发闪烁。
- 必须显式写成
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)(推荐使用这个缓动,接近系统原生开关手感) - 圆点容器(通常是
.switch)设为position: relative,圆点(.slider)设为position: absolute - 初始状态圆点左偏移为
left: 2px,开态改为left: calc(100% - 22px)(假设圆点宽 18px,留 2px 边距)
:checked + ~ 选择器联动圆点位置
纯 CSS 开关依赖 <input type="checkbox"> 的 :checked 状态,但 HTML 中 <input> 必须在圆点元素之前,才能用兄弟选择器 ~ 触发样式变更。顺序错了,动画根本不会动。
典型结构是:<input type="checkbox" id="mySwitch"> → <label for="mySwitch"></label> → 内部含 .slider。这样 #mySwitch:checked ~ .slider 才能生效。
立即学习“前端免费学习笔记(深入)”;
- 别用
+(相邻兄弟),因为<input>和.slider中间通常隔着其他标签或文本节点 - 如果用了 Vue/React 等框架,注意不要把
v-if或key放在<input>上,否则 DOM 节点复用失效,:checked状态无法触发 CSS 变更 - 移动端需加
-webkit-appearance: none清除默认样式,否则 iOS 会压掉自定义圆点
圆点“滚动感”的真实实现原理
所谓“滚动”,其实是视觉错觉:圆点本身没旋转,只是沿轨道水平移动 + 轨道背景色渐变配合。真正的滚动(rotate + translate)既难对齐又卡顿,没人这么干。
要模拟滚动惯性,核心是调 cubic-bezier 曲线。标准开关用 cubic-bezier(0.4, 0, 0.2, 1)(即 ease-in-out 的变种),关态加速快、开态收尾缓;若想更“跟手”,可试 cubic-bezier(0.25, 0.46, 0.45, 0.94)。
- 别用
steps()函数——那是做翻页动画的,用在这只会让圆点跳变 - 圆点尺寸必须固定(如
width: 18px; height: 18px;),否则translateX计算基准会漂移 - 轨道(
.switch)高度建议设为22px,圆点直径 18px,上下各留 2px 呼吸空间,视觉才稳
移动端点击区域与 focus 可访问性补漏
很多人做完动画就以为完了,结果在 iPhone 上要点两下才触发,或屏幕阅读器读不出开关状态——问题出在点击区域太小、没处理 :focus 样式。
iOS Safari 默认要求最小点击区域 44×44px,而开关轨道常只有 22px 高,必须用 padding 或 ::before 扩展命中区,同时保留视觉大小。
- 给
label加padding: 11px 0(上下各扩 11px),总点击高度变成 44px,不破坏布局 - 必须加
outline: 2px solid #007aff在:focus-visible上,否则键盘用户无法感知焦点位置 - 别用
user-select: none锁定文字——它会连带禁用 iOS 长按复制,影响辅助功能
圆点动画的难点不在位移逻辑,而在边界条件:不同缩放比下的像素对齐、深色模式下轨道颜色反差、以及 Safari 对 transform 合成层的偶发失效。这些没法靠一套 CSS 解决,得实机多测。










