前端开发者学堂 - fedev.cn

CSS vs SVG: 美化复选框和单选按钮

发布于 大漠

这是有关于CSS和SVG技术对比的系列文章第二篇,目的是通过示例解释两者的利弊,更好的帮助大家在Web制作中解决常见设计问题时能做出更好的选择。

上一篇文章中,我们讨论了使用CSS和SVG创建带纹理的文本效果,并得到结论是:目前阶段,使用SVG比使用CSS做更方便,更强大。在这篇文章中,我们将处理自定义的复选框和单选按钮的效果。

你可能已知道,使用CSS来美化表单的样式是一件不轻松的事情。但他们也不是特别的难搞。

在CSS中我们无法直接通过选择器来美化表单中的复选框和单选按钮,为了客服这一事实。前端开发人员中使用图片来美化。而在CSS要做到这一点,有两种方法。

使用CSS自定义复选框样式

使用一张雪碧图

使用CSS美化复选框和单选按钮最灵活的方式是通过一个雪碧图来实现,而这个雪碧图包括了两种状态:选中和未选中。

假设有一个表单同时有复选框和单选按钮,所以我们常常使用一张包含复选框和单选按钮两种状态的雪碧图。雪碧图的样式,看起来就像下图所示:

复选框和单选按钮

图像周边的区域是透明的。每个复选框或单选按钮的大小等于页面上的实际尺寸。这样一来,如果你想要的复选框在页面上显示的尺寸是25px x 25px时,你需要保证雪碧图上的图片按钮尺寸大小与其一样。

接下来是设置页面上的复选框,以便可以使用上面的雪碧图。

其结构看起来像这样:

<div>
  <input type="checkbox" id="option"/>
  <label for="option"> <span></span> Click me </label>
</div>

确保<label>标签的for属性的值与复选框的id属性的值一样。并且在<label>标签内有一个<span>标签,用来放置雪碧图。并不总是需要添加额外的标签来放置雪碧图,但我更喜欢这种方法来显示复选框,因为我们的雪碧图是水平排列。当然,你也可以根据你的爱好或需求,将雪碧图中的图标在垂直方向排列。

除了添加一个新元素<span>来放置雪碧图中的图标之外,还可以通过伪元素::before::after来放置雪碧图中的图标。

现在可以开始写样式代码了。

首先需要将复选框隐藏起来,并且在<label>设置一个cursor: pointer,表示label标签是可点击的:

input[type="checkbox"] {
    display: none;
}
label {
  cursor: pointer;
  color: #555;
}

接下来,指定<span>的大小和选择框想要的尺寸一样。在这个示例中,我希望复选框的大小是45px x 45px。我们还要将雪碧图当做背景应用到span上。

input[type="checkbox"] + label span {
    display: inline-block;
    vertical-align: middle;
    width: 45px;
    height: 45px;
    background: url(path/to/checkbox-radio-sprite.png) 0px center no-repeat;
}

注意,理想情况之下,你希望使用em来定义尺寸大小,这样会随着文本的大小做变化,但在这个示例中使用的是像素单位。

这些样式在span上使用了雪碧图,但只会显示雪碧图上的第一个未选中状态的图标(确保雪碧背景图的位置为0)。span是一个小区域,只会让雪碧图上部分在这个区域显示出来。

下面这张GIF动画图显示出了雪碧图在span中的位置是如何变化的。

复选框和单选按钮

背景图位置的偏移会影响背景图在span区域显示的效果,所以雪碧图只有部分在span区域显示。

接下来,使用:checked伪类来控制复选框选中状态时,label标签中span的样式:

input[type="checkbox"]:checked + label span {
    background: url(path/to/checkbox-radio-sprite.png) -48px center no-repeat;
}

那么有什么变化呢?我们唯一需要改变的就是背景图的位置。只要将span中的背景图位置设置为第二个图标的位置。我们确保使用雪碧图第二个复选框,只需要设置第二个图标的边缘。选中的复选框位置距离雪碧图边缘是48px。因此,为了确保span的背景图显示正确,只需要将背景图的位置设置为该值的负值(X轴设置为-48px)。

遵循同样的原理,未选中的单选按钮也可以按这样的方式调整背景图的位置。

复选框和单选按钮

如此一来,单选按钮的样式可以这样写:

input[type="radio"] + label span {
  background: url(path/to/checkbox-radio-sprite.png) -105px center no-repeat;
}

input[type="radio"]:checked + label span {
    background: url(path/to/checkbox-radio-sprite.png) -153px center no-repeat;
}

最终的效果如下所示:

你还可以看到类似的技术来处理复选框,但不是使用PNG图像,而使用的是CSS渐变图像。

使用CSS渐变

span不再使用PNG图像,而是使用CSS的线性渐变或径向渐变,让他们看起来像一个复选框或单选按钮。相比前一种方法,我更喜欢这种方法。但它也有一定的局限性,只能是一个纯色背景或是一个渐变。如果你需要一外更复杂效果的复选框,这种技术就很难做到了。

input[type="checkbox"] + label span,
input[type="radio"] + label span {
    display: inline-block;
    vertical-align: middle;
    width: 45px;
    height: 45px;
    border: 2px solid #888;
    border-radius: 10px;
    background: radial-gradient(#eee, #aaa);
}
/* we want the radio buttons to be circular */
input[type="radio"] + label span  {
  border-radius: 50%;
}

span的这些样式,它看起来像一个未选中的复选框或单选按钮效果。但对于选中状态,我们需要使用一个伪元素(或者添加一个子元素)来制作选中状态效果。

如果我们想给选中的复选框添加一个效果。要做到这一点,我们可以在span上使用一个伪元素,然后在其content中添加

input[type="checkbox"]:checked + label span::before {
   content: "✓";
   color: deepPink;
   text-align: center;
   font-size: 40px;
}

这样就实现了一个选中复选框的效果,是不是非常的简单。至于单选按钮,我们只需要在选中状态改变其背景颜色即可:

input[type="radio"]:checked + label span {
    background-image: radial-gradient(#FF5ABA, deepPink);
}

你可以改变单选按钮的span元素的伪元素样式,实现你想要的选中效果。

前面的示例最终效果如下所示:

正如你所看到的,这种技术较前一种技术来说更简单,但它没有太多的样式风格供你选择。

另一方面,我们可以使用SVG,为选中状态提供一个完全不同的显示方式。

使用SVG定义复选框样式

在选中和未选中两状态之间的切换,SVG为我们提供了一个更为自然和友好的方式来表现复选框或单选按钮选中和未选中状态的效果。因为SVG是一个图像,我们可以像前面介绍的CSS引用背景图像美化复选框样式的方法一样,使用SVG。只不过将SVG图像替换掉PNG图像。你甚至不需要这样做,你只需要在SVG图像中制作不同的状态(和PNG雪碧图方法几乎是相同的工作方式),只不过在SVG中是通过修改其viewBox属性值来实现。这也是这篇文章要介绍的一个部分。

有关于viewBox相关的知识,可以阅读:

然而,我们想利用SVG的互动添加一些更接近生活体验的交互效果,并且给这些效果添加一些平滑过渡的动画效果,让复选框和单选按钮的选中和未选中状态切换更贴进我们的真实生活。接下来的内容,我们一起来看看是如何做到这一点的。

首先,我们不需要多个SVG。一个SVG就足以实现这样的效果。然后,我们要做的第一件事情如前面介绍的一样,先把复选框(或单选按钮)隐藏起来,这样我们的SVG图像就可以显示。

input[type="checkbox"] {
    display: none;
}

然后,在我们的HTML结构中使用的不再是<span>标签元素,而是直接使用<svg>本身,并且将其放置在<label>标签元素内。接下来我们一起来看看代码是怎么写的。

<div>
  <input type="checkbox" id=“option”/>
  <label for="option">Click me
    <svg viewBox="0 0 60 40">
      <path d="M21,2 C13.4580219,4.16027394 1.62349378,18.3117469 3,19 C9.03653312,22.0182666 25.2482171,10.3758914 30,8 C32.9363621,6.53181896 41.321398,1.67860195 39,4 C36.1186011,6.8813989 3.11316157,27.1131616 5,29 C10.3223659,34.3223659 30.6434647,19.7426141 35,18 C41.2281047,15.5087581 46.3445303,13.6554697 46,14 C42.8258073,17.1741927 36.9154967,19.650702 33,22 C30.3136243,23.6118254 17,31.162498 17,34 C17,40.4724865 54,12.4064021 54,17 C54,23.7416728 34,27.2286213 34,37" id="Path-1" stroke-width="4" fill="none" stroke="orange" stroke-dasharray="270" stroke-dashoffset="-270">
      </path>
    </svg>
  </label>
</div>

SVG中的<path>元素看起来像一个手绘涂鸦效果(见下图)。我们要做的是点击<label>标签时让它看起来像是有人用钢笔在复选框上绘制。正如GIF动画展示的效果一样:

复选框和单选按钮

所以,我们的目标是svg上的路径动画效果,这样看起来好像是在绘画。为了做到这一点,需要使用@Jake ArchibaldSVG素描技术

如何使用SVG画线

两年前Jake在他的博客中写了一篇有关于SVG画线的技术Chris Coyier也在CSS-Tricks中一步一步的介绍了如何绘制线的动画,所以我建议你阅读这两篇文章更有助于下面例子的理解。为了简单起见,我直接使用了Jake文章中的GIF动画图,简单的向大家介绍他是如何工作的。

  • SVG中的元素的描边类似于HTML元素中的border。(如上面SVG代码中的stroke属性所示)
  • <path>中的stroke通常决定了<path>的形状。
  • SVG中的stroke可以是断点风格,就像HTML/CSS中border设置为dashed的风格
  • 在SVG中,你可以通过stroke-dasharray属性指定断线的长度和断点之间的间距。这需要两个值,而且使用空格隔开,用来指定一个破折号的长度,和指定长度间差距。如果你仅指定一个值,破折号和差距是具有一个相同的长度值。

复选框和单选按钮

破折号的长度和整个路径的长度一样。

复选框和单选按钮

路径的破折号可以设置一个偏移量用来设置破折号位置,其沿着路径自己调节间各自间的间距。下面的图显示了路径与多个破折号之间的转换效果:

复选框和单选按钮

改变了偏移量的破折号和路径的长度还是一样的相等。注意下图,破折号并没有变短,只是破折号之间的间距变得更大。

复选框和单选按钮

现在,为了模拟素描效果,给破折号偏移设置动画。

复选框和单选按钮

注意,示例显示了stroke-dashoffset值从全长(jake的示例是988)变到0的动画。

这里阐述了画线是如何工作的。路径的描边是一个破折号,破折号的长度等于路径自身的长度,那么破折号的偏移量设置了路径的长度,慢慢过渡到0的一个动画效果。这样很好,对吗?

现在,由于素描的工作方式是,前面的SVG代码片段,使用破折号的偏移量值为路径长度270,来替代stroke-dashoffset值为0

然后当复选框选中时设置stroke-dashoffset值为0。为了动画有一个平滑过渡效果,而不是从路径的一种状态突然跳到另一种状态,需要添加一个transition样式。

label svg path {
  transition: stroke-dashoffset .4s linear;
}
input[type="checkbox"]:checked ~ label svg path {
  stroke-dashoffset: 0;
}

最终的效果如下所示:

单选按钮也可以使用相同的方式来处理,只是使用圆形来替代矩形。@Mary Lou创建了一组复选框和单选按钮的动画效果,其采用的方法是相同的。下面的GIF动图演示了整个示例的效果:

复选框和单选按钮

可以点击这里查看动画效果。

这种方法的唯一缺点是,它不能在IE上运行,因为IE不支持SVG的CSS动画。在Edge将会改变这个现像,但在旧版的IE上仍然无法得到支持,所以你需要一个没有动画的版本或使用JavaScript给路径添加动画。

你经常会发现自己需要JavaScript创建SVG的素描效果,因为在大多数情况下,你可以无法准确知道动画的路径的长度。你可以通过检索SVG路径的长度值,然后计算破折号的偏移量进行计算。下面是Jake文章中提供JavaScript代码片段:

var path = document.querySelector('svg path');
path.getTotalLength(); // gets the length of the path
// Set up the starting positions
path.style.strokeDasharray = length + ' ' + length;
path.style.strokeDashoffset = length;

// Define our transition
path.style.transition = path.style.WebkitTransition =
  'stroke-dashoffset 2s ease-in-out';
// Go!
path.style.strokeDashoffset = '0';

你可以在Jake的文章找到完整的代码,并且还能看到整个交互效果。

总结

如果你问我,我会告诉你没有最好的,只有更好的。每一个都有其利弊。CSS技术都不错,SVG技术增加了一个漂亮和熟悉的交互效果,让人感觉更舒服,但这并不是说CSS技术给用户的体验不好。

SVG有许多灵活性、创造性,这对你来说无疑是一个巨大的优势。另外,如果你想要,你总是能将利用SVG雪碧图像替代PNG雪碧图像。毕竟,SVG看起来更好。

根据你自己设计要求和浏览器兼容性选择适合你自己的技术。我希望这篇文章能帮助你做出最好的选择。感谢您的阅读。

本文根据@Sara Soueidan的《CSS vs SVG: Styling Checkboxes and Radio Buttons》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:https://blogs.adobe.com/dreamweaver/2015/08/css-vs-svg-styling-checkboxes-and-radio-buttons.html