iOS 开关按钮

1,623 阅读7分钟

大家好,我是 Steven。

这期我们会用 CSS 制作一个 iOS 的开关按钮,先看看制作的成品:

点击一下会打开,再点击一下会关闭。但原来它还有一个小细节,就是长按的时候里面的圆形会拉长,放手后会变回圆形:

还有就是现在流行夜间模式 (Dark Mode),这个按钮会因应系统的色调设定改变,试试将系统改变为夜间模式:

按钮的背景颜色就会改为深灰色。

在这个案例中,我们会学习到如何使用 CSS 建构这个按钮,过程中会运用到 CSS 的变数,以及如何判断系统的色调设定,套用夜间模式时的 CSS 样式。

这个教程的视频版本在 www.bilibili.com/video/BV1t7… ,欢迎三连关注!

分析按钮

首先看看这个 iOS 开关按钮所有状态的样式,这是我从 iOS 中截图,然后在 Sketch 中按一比一绘画的。

左边是 Light Mode 下的样式,而右边是 Dark Mode 下的样式。Light Mode 与 Dark Mode 下的开关按钮,就只有在关闭状态时的灰色不同,Light Mode 下的是浅灰色,Dark Mode 下的是深灰色。

由于建构这个按钮时使用到的数值比较多,例如按钮的宽度与高度,里面圆形在正常与拉长状态下的大小,以及绿色、灰色、深灰色等多种颜色。如果可以将这些设定值都放到同一个地方,那么将来要改变这个按钮的参数也很容易,这就可以用上 CSS 变数。

CSS 变数的定义方法,是使用两个 - 符号 (--) 作起首,然后就可以定义变数的名称,这里我预先规划好了所有会定义的变数名称:

  • --button-width--button-height 按钮的宽度与高度
  • --toggle-diameter 里面圆形的直径
  • 红色这个 --button-toggle-offset 是按钮与里面圆形的间距,这可以通过 --button-height 减去 --toggle-diameter 再除以 2 计算出来
  • --toggle-shadow-offset 里面圆形的阴影打多大
  • --color-grey 按钮的浅灰色背景
  • --toggle-wider 里面圆形在长按的状态下拉宽后的宽度
  • --color-dark-grey 夜间模式时的深灰色背景
  • --color-green 按钮在开启状态时的绿色背景

所有将会定义的变数以及设定值已整理到这个列表中:

定义 CSS 变数

然后我们就根据这个列表,将所有变数都定义到 CSS 中。首先定义一个名为 :root 的选择器,:root 可以理解为最顶层,将 CSS 变数定义在这里,代表可以在任何任置都存取得到。

然后一一输入变数名称与它的值。--button-toggle-offset 这个值是通过 --button-height--toggle-diameter 计算出来的。先通过 var(--button-height) 获得 --button-height 的值,再通过 var(--toggle-diameter) 获得 --toggle-diameter 的值,由于要将它们相减,所以在外层套用 calc() 函数,两个变数之间再加上减号,由于先乘除后加减的关系,将相减的运算式再加一层括号,然后在 calc() 的关括号前加上 / 2 就可以了。

现在变数已定义好了,接着下来就开始建构这个按钮。

建构开关按钮

在 HTML 内新增一个 <span> 标签,然后在 CSS 中加入 span 选择器,displayinline-block。宽度 widthvar(--button-width),而高度 heightvar(--button-height)

背景颜色 background-colorvar(--color-grey)。圆角 border-radius 是按钮高度的一半,所以先用 var(--button-height) 获取它的值,再通过 calc() 包着它,然后除以 2。

而里面的圆形因为会使用 position: absolute 去定位,所以这里会将 position 设定为 relative

将这个按钮在页面中上下左右居中,加入 body 选择器,display 设定为 flexjustify-contentalign-items 设定为 centermin-height 设定为 100vh

然后设定里面的圆形,我会使用 Pseudo Selector(伪类)。加入 span::after 选择器,content 设定为空白字串,这样浏览器才会将它显示出来。

然后将 display 设定为 inline-block,宽度与高度设定为 --toggle-diameter,背景颜色设定为白色,然后 border-radius 是直径的一半,所以是 calc(),里面填上 var(--toggle-diameter) 然后除 2。

现在圆形已经显示了出来,将 position 设定为 absolute,再将 top 设定为 --button-toggle-offset,对于左边的偏移距离,为了方便稍后做动画,我会使用 transformtranslateX() 将它移动,设定值同样是 var(--button-toggle-offset)

再设定阴影,box-shadow 的 X 方向距离是 var(--toggle-shadow-offset),Y 方向是 0,然后 blur(阴影的模糊)设定为距离的 4 倍,所以是 calc(var(--toggle-shadow-offset) * 4)

最后,颜色设定为 10% 半透明的黑色,使用 rgba(0, 0, 0, .10) 就可以了。

处理按钮点击

处理这个按钮的点击事件,并不需要使用到 JavaScript,只需运用 HTML 的表单元素 checkbox 配合 CSS 即可。

在 HTML 中新增 <label> 标签,里面加上 <input type="checkbox">,由于按钮被 <label> 包着的关系,现在点击这个按钮就可以同时点击到 checkbox。

然后通过 input[type="checkbox"]:checked + span,控制 checkbox 在勾选状态下按钮的样式。先把背景颜色设定为 var(--color-green)。现在点击按钮,背景颜色就会因应 checkbox 的勾选状态而改变。

再调整里面的圆形的位置,通过 input[type="checkbox"]:checked + span::after,将 transform 设定为 translateX(),按钮宽度减去里面圆形的直径,再减去按钮与圆形间的距离。即 calc()var(--button-width) 减去 var(--toggle-diameter),再减去 var(--button-toggle-offset))

再将阴影调整一下,当圆形在右边的时候,阴影改为左边,复制一下本身的 box-shadow 设定,然后将 var(--toggle-shadow-offset) 乘以 -1 即可:

接下来在 spanspan::after 都加上 transition 设定,加入 transition: .3s all ease-in-out;,为背景颜色与圆形加上平滑过渡的动画:

现在可以将 checkboxdisplay 设定为 none 将其隐藏:

处理长按时的效果

现在开关按钮已经基本完成,接下来加上长按状态下的拉长效果。

按下的状态可以通过 :active 去控制,加上 input[type="checkbox"]:active + span::after,即按下 checkbox 时,里面的圆形的样式。将 width 设定为 var(--toggle-wider)

左至右的效果没有问题,而右至左则超出了按钮的范围,这需要再处理 input[type="checkbox"]:checked:active + span::after,即 checkbox 在已勾选的状态下,长按时里面的圆形的样式。

超出按钮范围的原因是,按钮的宽度改变了,所以需要把它向左移一点。将 transform 设定为 translateX(),按钮宽度 --button-width 减去加长了的圆形宽度 --toggle-width,再减去按钮与圆形之间的距离 --button-toggle-offset

这样就完成了。

夜间模式

最后,加上夜间模式的处理。我们可以通过 CSS 的 Media Query @media (prefers-color-scheme: dark) 去判断当前的系统是不是启用了夜间模式。

而在 Media 区块内的,就是夜间模式下会套用的 CSS 样式设定,首先将 body 的背景颜色设定为深灰色 #1C1C1E,再将 span 按钮的背景颜色设定为深灰色 var(--color-dark-grey)

我们来看看这个案例的完成效果

以上,就是这期要介绍的全部内容。


这个案例的源代码在 codepen.io/stevenlei/p…

你的支持是我的动力,请关注 CodingStartup 起码课,我们一起加油!