梯形标签
最后更新于:2022-04-01 03:43:56
> 原文出处:http://www.w3cplus.com/blog/1658.html
## 问题
梯形应用得比[平行四边形](http://www.w3cplus.com/css3/css-secrets/parallelograms.html)更普遍:只有两条边是平行的。另外两条可以是任何角度。以前,它们都是CSS中很难创建的形状,尽管它们特别常用,特别是对于标签。作者要么是通过精心设计的背景图像来模拟它们,要么是一个矩形旁边带两个三角形来创建,或者是通过边框来伪造一下。
![梯形标签](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d2024305d.png "梯形标签")
*图注:通过伪元素边框伪造的梯形(为清楚起见,用较暗的蓝色表示伪元素)*
尽管这种技术可以节省我们花费在图像上的额外的HTTP请求,也可以非常简单地调整宽度,但还是不理想。这既浪费了可用的伪元素,在样式上也非常不灵活。比如,要添加一个边框,一个背景纹理,或一些标签周围的东西的时候就悲剧了(>﹏<)。
[![梯形标签](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d20cae6de.png "梯形标签")](https://c9.io/)
> [Cloud9 IDE](https://c9.io/)每个打开的文档都有梯形的标签
[![梯形标签](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d20ce82f9.png "梯形标签")](https://css-tricks.com/)
> [csstricks.com](https://css-tricks.com/)早期的梯形便签,尽管只倾斜了一个方向
因为几乎所有用于梯形设计的技术都非常混乱甚至难以维护,我们在Web上看到的大多不是倾斜的,尽管现实的标签是这样的。是否有一个完整的灵活的方式来创建梯形标签呢?
## 解决方案
是否存在这样的可以创建梯形的2D变换的组合,我们只需要应用[平行四边形](http://www.w3cplus.com/css3/css-secrets/parallelograms.html)中的解决方案稍微转变一下,就可以完成了。可惜,事情并不是这么简单。
想象在一个物理的三维世界里旋转一个矩形。我们看到的二维图像通常是一个梯形,因为角度问题!所以,我们可以通过使用一个3D旋转来在CSS中模拟这个效果:
~~~
transform: perspective(.5em) rotateX(5deg);
~~~
![梯形标签](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d20d53d68.png "梯形标签")
*图注:通过3D旋转创建一个梯形。上边:变换前;下边:变换后*
你可以在上图中看到它创建出的梯形。当然,因为我们给一整个元素都应用了3D变换,文本也失真了。3D变换不能像2D变换那样,将内部文本的变换抵消(因为2D可以通过一个相反方向的变换来抵消变换)。从技术上将内部元素的变换取消是可行的,但是非常复杂。因此,唯一实用的方式就是利用3D变换来创建一个梯形,把这种变换应用到伪元素上,类似于[**平行四边形**](http://www.w3cplus.com/css3/css-secrets/parallelograms.html)中的方法:
~~~
.tab {
position: relative;
display: inline-block;
padding: .5em 1em .35em;
color: white;
}
.tab::before {
content: ''; /* To generate the box */
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
z-index: -1;
background: #58a;
transform: perspective(.5em) rotateX(5deg);
}
~~~
![梯形标签](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d217e6579.png "梯形标签")
*图注:给伪元素生成的盒子应用3D变换,这样我们的文本就不会受到影响*
如上图所示,这可以创建出一个基本的梯形。虽然还有一个问题,当我们在应用的变换没有设置`transform-origin`,元素会围绕其中心旋转。因此,我们屏幕上的这个2D的投影会因为很多因素改变,如下图所示:
![梯形标签](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d218208e2.png "梯形标签")
*图注:我们的梯形覆盖在其预变换的版本上,以突出其指标的改变*
当宽度增加时,它会向上移动,在高度上稍微有点变小等,这使得它很难设计。
为了让这个指标更可控,我们指定了`transform-origin: bottom;`,这样在旋转的时候它的基本还是固定的。你可以在下图中看到区别。
![梯形标签](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d21ad7a32.png "梯形标签")
*图注:我们覆盖在预变换版本上的梯形,当使用`transform-origin: bottom;`时将尺寸变化高亮*
现在它更可预见一些:只有高度减少了。但是,高度的减少是非常清晰的,因为整个元素都旋转到远离观察者了,而在此之前,它有一半在屏幕之上,一半在屏幕之下,这样整个元素在三维空间里离观察者更近一些。为了解决这个问题,我们可能会想给它应用额外的顶部内边距。但是,浏览器中的显示结果还是非常糟糕,因为没有支持3D变换(如下图所示)。
![梯形标签](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d21b180fe.png "梯形标签")
*图注:使用额外的`padding`解决问题导致了一个非常奇怪的降级*
相反,我们可以通过一个变换来增加它的尺寸,这样当不支持3D变换的时候,整个内容都会失效。经过几个试验,我们发现约`130%`的垂直缩放(如`scaleY()`变换)足以弥补失去的空间:
~~~
transform: scaleY(1.3) perspective(.5em) rotateX(5deg);
transform-origin: bottom;
~~~
![梯形标签](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d2204835d.png "梯形标签")
*图注:使用`scale()`来弥补失去的高度,提供了一个非常好的降级(上方的图)*
你可以在上图中看到结果和降级。这里,结果只是在视觉上等同于前面提到的基于`border`的技术,只是这里的语法更简洁。但是,当你开始给标签应用一些样式的时候,这种技术的优势开始出现。例如,看看下面的代码:
~~~
nav > a {
position: relative;
display: inline-block;
padding: .3em 1em 0;
}
nav > a::before {
content: '';
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
z-index: -1;
background: #ccc;
background-image: linear-gradient(
hsla(0,0%,100%,.6),
hsla(0,0%,100%,0));
border: 1px solid rgba(0,0,0,.4);
border-bottom: none;
border-radius: .5em .5em 0 0;
box-shadow: 0 .15em white inset;
transform: perspective(.5em) rotateX(5deg);
transform-origin: bottom;
}
~~~
上面代码的效果如下图所示:
![梯形标签](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d2208b8d2.png "梯形标签")
*图注:这种技术的优势在于它样式方面的灵活性*
如你所见,我们已经应用了背景、边框、圆角,还有盒阴影——它们都是可行的,没有任何问题!此外,只需要把`transform-origin`的值改为`bottom` `left` 或 `bottom` `right`,我们就可以得到向左或向右倾斜的标签,分别!
![梯形标签](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d220ba985.png "梯形标签")
*图注:通过改变`transform-origin`的值生成的斜标签*
尽管它有这么多的优点,这种技术还是不够完美。它有一个非常重大的缺陷:侧边的角度取决于元素的宽度。因此,当处理不同的内容时,用相同的角度来得到梯形是很棘手的。但是,对于宽度变化小的元素,它还是非常ok的,比如导航菜单。在这些情况中,差异是难以察觉的。
斜切角
最后更新于:2022-04-01 03:43:54
> 原文出处:http://www.w3cplus.com/css3/css-secrets/cutout-corners.html
## 问题
斜切角在Web设计和印刷中是相当受欢迎的样式。它通常是在一个或多个元素的角落切一个`45°`的角(也就是所谓的斜切角)。特别是最近,扁平化设计的势头压过了拟真设计,也使这种效果更加流行。当斜切角只存在元素的一侧,并且每个都占据元素的`50%`高度的时候,一个箭头的形状产生了,这在按钮和面包屑导航中非常受欢迎。
![斜切角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d18360999.png "斜切角")
*图注:带斜切角的按钮,创建了箭头形状强调其意义*
但是,要用CSS来创建这个效果并不是那么容易,这不是一行代码就可以搞定的效果。这导致很多作者倾向于直接使用背景图像完成,而不是结合三角形来完成斜切角(当背景是纯色),也不会去使用一个或多个角落已经被切的图像来作为整个背景。
![斜切角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d18395507.png "斜切角")
*图注:应用了斜切角的网站示例(半透明的“Find & Book”盒子的左下角)*
这种方法显然是不灵活的,而且难以维护,还增加了延迟,因为增加了HTTP请求和网站的总文件大小。有没有什么更好的方法呢?
## 解决方案
第一个解决方案是万能的CSS渐变。我们假设我们暂时只完成一个斜切角——右下角那个。其诀窍在于渐变可以接受一个角度值作为参数(如`45deg`),色标位置是绝对长度,这两个都不会因为元素和背景尺寸的改变受到影响。
综上所述,我们只需要一个线性渐变就ok了。它需要一个透明的色标作为斜切角,还有另一个相同位置的带有我们想要作为背景颜色的色标。CSS代码如下(如一个`15px`大小的切角):
~~~
background: #58a;
background: linear-gradient(-45deg, transparent 15px, #58a 0);
~~~
很简单,对不对?你可以看到结果。
![斜切角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d1841859c.png "斜切角")
*图注:右下角斜切的元素,通过一个简单的CSS渐变完成*
从技术上说,我们甚至不需要第一条声明。我们只是把它作为一个降级引入:当CSS渐变不被支持时,也就是第二条声明失效的时候。所以我们还是需要一个纯色背景。
> 为方便调试,我们使用不同的颜色(`#58a`和`#655`)。在应用中,两个渐变应该是相同的颜色。
现在,假设我们需要两个斜切角,左右下角分别一个。只用一个渐变是没办法搞定的,所以两个渐变上场。我们首先想到的可能是:
~~~
background: #58a;
background: linear-gradient(-45deg, transparent 15px, #58a 0),
linear-gradient(45deg, transparent 15px, #655 0);
~~~
但是,如下图所示,这是不ok的。
![斜切角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d1844cca0.png "斜切角")
*图注:给底部两个角都应用斜切角效果的失败尝试*
默认情况下,两个渐变被应用于同一个元素,它们会相爱相杀(彼此掩盖)。我们需要把它们变小,通过使用`background-size`来让每个渐变都只应用于元素的一半:
~~~
background: #58a;
background: linear-gradient(-45deg, transparent 15px, #58a 0) right,
linear-gradient(45deg, transparent 15px, #655 0) left;
background-size: 50% 100%;
~~~
你可以在下图中看到结果。
![斜切角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d18475ec7.png "斜切角")
*图注:只一条`background-size`是不够哒~*
即使应用了`background-size`,这俩渐变还是会把对方盖住。因为我们忘了把`background-repeat`关掉了,所以我们的背景就都重复了两次。因此,我们的背景还是在相爱相杀——这次是因为背景重复。新修改的代码如下:
~~~
background: #58a;
background: linear-gradient(-45deg, transparent 15px, #58a 0) right,
linear-gradient(45deg, transparent 15px, #655 0) left;
background-size: 50% 100%;
background-repeat: no-repeat;
~~~
你可以在下图中看到结果。
![斜切角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d184ce9de.png "斜切角")
*图注:我们的左下和右下斜切角终于ok了*
它终!于!通过验证了!现在,你应该知道要如何把这个效果应用到其它四个角了吧。一共需要四个渐变,代码如下:
~~~
background: #58a;
background: linear-gradient(135deg, transparent 15px, #58a 0) top left,
linear-gradient(-135deg, transparent 15px, #655 0) top right,
linear-gradient(-45deg, transparent 15px, #58a 0) bottom right,
linear-gradient(45deg, transparent 15px, #655 0) bottom left;
background-size: 50% 50%;
background-repeat: no-repeat;
~~~
你可以在下图中看到结果。
![斜切角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d18513bb8.png "斜切角")
*图注:使用四个渐变,给四个角都应用了效果*
前面的代码有个问题是,它不是特别可维护的。要改变背景颜色还有四个角的大小,需要五次编辑。加入预处理器mixin有助于减少重复。这是SCSS代码:
~~~
@mixin beveled-corners($bg, $tl:0, $tr:$tl, $br:$tl, $bl:$tr) {
background: $bg;
background: linear-gradient(135deg, transparent $tl, $bg 0) top left,
linear-gradient(225deg, transparent $tr, $bg 0) top right,
linear-gradient(-45deg, transparent $br, $bg 0) bottom right,
linear-gradient(45deg, transparent $bl, $bg 0) bottom left;
background-size: 50% 50%;
background-repeat: no-repeat;
}
~~~
然后,在需要的时候,它可以像这样被使用,使用`2-5`个参数:
~~~
@include beveled-corners(#58a, 15px, 5px);
~~~
在这个示例中,我们将会得到在左上角和右下角得到`15px`斜切角,在左下角和右上角得到`5px`斜切角,和`border-radius`的原理相似。这是因为我们为SCSS的mixin的参数提供了默认值,还有,这些默认值也可以引用其它的参数。
## 曲线切口角
有很多渐变的方法可以用来创建曲线切口角,大家常常把这个效果称之为“内圆角”,因为它看起来就是圆角的反相版本。和斜切角唯一的不同是它使用了径向渐变,而不是线性渐变:
[![斜切角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d18a926ed.png "斜切角")](http://g2geogeske.com/)
> [g2geogeske.com](http://g2geogeske.com/)上使用曲线切口角的非常棒的例子;设计师已经把它们变成核心设计元素,因为它们存在于导航、内容、甚至页脚中。
~~~
background: #58a;
background: radial-gradient(circle at top left,
transparent 15px, #58a 0) top left,
radial-gradient(circle at top right,
transparent 15px, #58a 0) top right,
radial-gradient(circle at bottom right,
transparent 15px, #58a 0) bottom right,
radial-gradient(circle at bottom left,
transparent 15px, #58a 0) bottom left;
background-size: 50% 50%;
background-repeat: no-repeat;
~~~
你可以在下图中看到结果。
![斜切角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d195328ef.png "斜切角")
*图注:使用径向渐变完成的曲线切口角*
和前面的技术一样,切口角的大小可以通过色标的位置进行控制,使用mixin可以让代码更易于维护。
## 内联SVG、border-image的解决方案
虽然基于渐变的解决方案是可行的,但是它有几个问题:
* 代码非常长,而且重复。在通常情况下,我们希望在四个角都有相同尺寸的切口角,这样我们就需要重复四次编辑。相似的,要修改背景颜色我们也需要四次编辑,五个计算降级。
* 在不同尺寸的切口角之间设置动画是完全不可能的(依赖于浏览器)。
幸好,根据我们的需求,还有几个我们可以采用的方法。一个是在内联SVG中使用`border-image`生成圆角。根据[`border-image`](http://www.w3cplus.com/content/css3-border-image)的工作原理,你可以想象一下我们的SVG会如何吗?
因为尺寸并不重要(`border-image`可以缩放,而且SVG缩放是完美的,不需要担心尺寸——这就是矢量图的好处!),单位可以是`1`,或者更直接的,直接是数字。拐角长度可以是长度`1`,直边长度也为`1`。结果(缩放)如下图所示。
![斜切角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d1956b44d.png "斜切角")
*图注:基于SVG的`border-image`,及其切片*
代码如下:
~~~
border: 15px solid transparent;
border-image: 1 url('data:image/svg+xml,\<svg xmlns="http://www.w3.org/2000/svg" width="3" height="3" fill="%2358a">\<polygon points="0,1 1,0 2,0 3,1 3,2 2,3 1,3 0,2"/>\</svg>');
~~~
注意我们使用了大小为`1`的切片。这不是`1`像素的意思,它指的是SVG文件的坐标系(因此缺省单位)。如果我们指定了它的百分比,我们需要的就是`1/3`图像的近似值,如`33.34%`。近似值是有风险的,因此不是所有的浏览器都使用相同的精确度。但是,通过使用SVG文件的坐标系统的单位,我们就不需要纠结精确值的问题了。
![斜切角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d195a4f50.png "斜切角")
*图注:给`border-image`属性应用SVG*
结果如上图所示。我们的切口角有了,但是没有背景。你可以通过两个方法解决:指定一个背景,或者给我们的`border-image`声明添加一个关键字`fill`,这样它就不会丢弃中间的切片。这里,我们选择指定一个背景,因为这样也可以作为一个降级。
另外,我们的切口角比前面创建的切口角要小,有点奇怪。我们明明指定了一个`15px`的边框宽度!这是渐变造成的,`15px`沿着渐变方向的,而方向垂直于梯度。同时边框的宽度不是对角线测量的,而是水平/垂直方向的。所以你找到原因了吗?对,我们要再次使用勾股定理,我们在前面也讲过。
![斜切角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d195d40b0.png "斜切角")
*图注:指定`border-width`为`15px`,得到结果为`15 / 根号(2) ≈ 10.606601718`的拐角尺寸,这也是为什么我们的拐角看起来小很多*
上图有助于我们理解。总而言之,为了得到相同的尺寸,我们使用的边框应该是我们用渐变方法的尺寸的`根号(2)`倍。这里,应该是`15 * 根号(2) ≈ 21.213203436`像素,也就是约等于`20px`,除非我们真的需要对角线尽可能地接近于`15px`:
~~~
border: 20px solid transparent;
border-image: 1 url('data:image/svg+xml,\<svg xmlns="http://www.w3.org/2000/svg" width="3" height="3" fill="%2358a">\<polygon points="0,1 1,0 2,0 3,1 3,2 2,3 1,3 0,2"/>\</svg>');
background: #58a;
~~~
![斜切角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d19619312.png "斜切角")
*图注:我们的切口角哪去了?!*
但是,上图的效果并不是我们期待的效果。我们辛辛苦苦创建的切口角躲到哪里去了?年轻人,不要担心!切口角还在那里。如果你把背景设置成其它的颜色,如`#655`,你就会知道是怎么回事了。
![斜切角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d1963d21e.png "斜切角")
*图注:把我们的`background`换成其它颜色,就可以发现神秘消失的切口角了*
上图所示,我们的切口角消失的原因是我们指定的背景把它们挡住了。我们需要做的是使用`background-clip`来阻止背景扩展到覆盖了我们的边框:
~~~
border: 20px solid transparent;
border-image: 1 url('data:image/svg+xml,\<svg xmlns="http://www.w3.org/2000/svg"\ width="3" height="3" fill="%2358a">\<polygon points="0,1 1,0 2,0 3,1 3,2 2,3 1,3 0,2"/>\</svg>');
background: #58a;
background-clip: padding-box;
~~~
这个问题已经解决,我们现在的盒子如下图所示。
![斜切角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d18513bb8.png "斜切角")
但是,我们可以只在一个地方很容易地改变拐角的尺寸:我们只需要修改边框的宽度。我们甚至可以给它添加动画,因为`border-width`是可添加动画的!我们还可以只通过两次编辑就改变背景。另外,因为我们的背景现在是独立于拐角效果的,我们甚至可以给它指定一个渐变,或其它的纹理,只要它在边缘处的颜色还是`#58a`就好。例如,看看下图,使用了一个从`hsla(0,0%,100%,.2)`到`transparent`的径向渐变。
![斜切角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d1969ae1f.png "斜切角")
*图注:带有径向渐变背景的切口角*
只剩下一个小问题了。如果`border-image`不被支持,降级不仅没有拐角。因为背景裁剪,它看起来还像是盒子边缘和内容之间没有任何填充空间。为了解决这个问题,我们可以给我们的边框添加一个和背景相同的颜色:
~~~
border: 20px solid #58a;
border-image: 1 url('data:image/svg+xml,\<svg xmlns="http://www.w3.org/2000/svg"\ width="3" height="3" fill="%2358a">\<polygon points="0,1 1,0 2,0 3,1 3,2 2,3 1,3 0,2"/>\</svg>');
background: #58a;
background-clip: padding-box;
~~~
当`border-image`应用的时候,颜色会被忽略,这可以提供一个更优雅的降级,看起来如图所示。
![斜切角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d19619312.png "斜切角")
因为这个缺陷,使得我们想要改变背景颜色的时候,需要的编辑次数从`2`变成了`3`。
## `clip-path`的解决方案
因为`border-image`的解决方案非常紧凑而且相对DRY,它仍然有其局限性。例如,我们仍然需要有一个纯色背景,或一个在边缘处带有纯色的背景。如果我们想要一种不同的背景呢,例如纹理、图案,或一个线性渐变?
有一个方法可以使我们不受所有的这些限制,尽管它也有自己的局限性。还记得`clip-path`属性吗?一件非常令人惊叹的事情是,CSS的`clip-path`可以结合百分比(这里指的是元件尺寸)以及绝对长度使用,给了我们极大的灵活性。
例如,`clip-path`裁剪一个带`20px`(水平方向)切角的矩形元素的代码如下所示:
~~~
background: #58a;
clip-path: polygon( 20px 0, calc(100% - 20px) 0, 100% 20px,
100% calc(100% - 20px), calc(100% - 20px) 100%,
20px 100%, 0 calc(100% - 20px), 0 20px
);
~~~
尽管代码很短,也不意味着它是DRY的,这就是它本身最大的问题之一,如果你不使用预处理器的话。事实上,这是我们提出的最WET的CSS解决方案,要改变拐角尺寸需要`8`次编辑!但是,我们只需要在一处位置改变背景。
它的优势是我们可以使用任何我们想要的背景,甚至是裁剪像图片这样的替换元素。如下图带有切角的图像。
![斜切角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-08_5615d1971e4d3.png "斜切角")
*图注:通过`clip-path`制作的带有切角的图片*
前面的几种方法都不能完成。另外,`clip-path`是可动画的,即使斜切角的大小不同,形状也不同。我们需要做的只是使用一个不同的裁剪路径。
除了WET,它的浏览器支持也不够,它还有个缺点是:如果没有提供足够的`padding`,它还会裁剪文本,因为它只裁剪元素,而不区分都是哪些部分。与此相反,渐变的方法只是让文本在超出拐角(因为它们只是背景),这样`border-image`方法只是扮演边框的角色,来包裹文本。
### 将来的拐角
将来我们不需要再通过CSS渐变、裁剪或SVG来完成这种效果了。一个新属性,`corner-shape`即将加入到[CSS Backgrounds & Borders Level 4](http://dev.w3.org/csswg/css-backgrounds-4/)中,可以帮我们节省很多精力。它可以和`border-radius`结合使用,用于产生不同切口角效果,在`border-radius`中定义尺寸即可。如,在所有角落指定`15px`的切口角将会很简单:
~~~
border-radius: 15px;
corner-shape: bevel;
~~~
菱形图片
最后更新于:2022-04-01 03:43:51
> 原文出处:http://www.w3cplus.com/css3/css-secrets/diamond-images.html
## 问题
用菱形裁剪图片在视觉设计中非常常见,但是这种效果没办法直接用CSS完成。实际上,直到最近,这基本上还是不可能做到的。因为,当网页设计师想要使用这种样式,他们通常会先用图像编辑器预裁剪图像。当然,不用想都知道,这对于应用任何效果都不是一种可维护的方式,如果后面有人想要改变图像样式,最后的结果都会变得非常混乱。
当然,现在有一个更好的方法,对吧?实际上,有两个耶!
![菱形图片](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e84c5ef0b.png "菱形图片")
*图注:这是2013年的设计,24ways.org现在用菱形裁剪展示作者的简介图片,使用了这里讨论的技术*
## 基于变换的解决方案
主要的思想和在[上一节“多边形”中的第一个解决方案](http://www.w3cplus.com/css3/css-secrets/parallelograms.html)一样,我们需要用一个`<div>`包裹我们的图像,然后给它们应用相反的`rotate()`变换:
~~~
<div class="picture">
<img src="adam-catlace.jpg" alt="…" />
</div>
.picture {
width: 400px;
transform: rotate(45deg);
overflow: hidden;
}
.picture > img {
max-width: 100%;
transform: rotate(-45deg);
}
~~~
![菱形图片](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e84ca2f12.png "菱形图片")
*图注:我们的原始图像,我们将要把它裁剪成菱形*
但是,下图所示效果这并不是我们想要完成的效果。
![菱形图片](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e84d1c726.png "菱形图片")
*图注:反方向的`rotate()`不足以完成这个效果(`.picture`div用一个虚线框标出)*
除非我们是要把图像裁剪成八边形的形状,这样我们现在就可以停下来,然后去做其它的事情。如果是把它裁剪成菱形,我们还有很多工作要做。
主要是`max-width: 100%`的声明。`100%`对应的是我们的`.picture`容器的边长。但是,我希望我们的图片和它的对角线一样长,而不是它的边。你可能已经猜到了,没错我们要再次使用勾股定理了(如果你需要复习,在[第二章第五节的“Diagonal stripes”一小节中](http://www.w3cplus.com/css3/css-secrets/striped-backgrounds.html)有提到)。定理告诉我们,方形的对角线等于它的边长乘以`根号(2)≈1.414213562`。因此,把它的`max-width`设置为`根号(2)×100%≈141.4213562%`,约等于`142%`。因为我们不希望它在任何哪种情况下会变小(但稍微大一点是可以接受的,因为我们的图像是被裁剪的)。
事实上,通过`scale()`变换来放大元素,有以下几个原因:
* 我们希望图片的大小保持`100%`,如果CSS变换没有被支持
* 通过`scale()`变换来放大图像可以将它从中心放大(除非另外指定了一个不同的`transform-origin`值)。如果是使用`width`属性来直接进行大小的调整,那么它会从左上角开始放大,这样我们最后还要使用负边距来移动它。
综上所述,我们最后的代码如下:
~~~
.picture {
width: 400px;
transform: rotate(45deg);
overflow: hidden;
}
.picture > img {
max-width: 100%;
transform: rotate(-45deg) scale(1.42);
}
~~~
下图所示效果,这就是我们想要的效果。
![菱形图片](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e84d4ae14.png "菱形图片")
*图注:最后裁剪出的图像*
## 基于`clip-path`的解决方案
前面的解决方案是可行的,但是它基本上是hack。它需要额外的HTML元素,而且也很混乱、复杂,并且脆弱:如果碰巧我们要处理的不是正方形图像,最终的结果就会非常糟糕:
![菱形图片](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e84e44f54.png "菱形图片")
*图注:如果处理的是非正方形的图像,基于变换的解决方案将变得非常糟糕*
实际上,还有一个更好的方法。主要的思想是使用[`clip-path`](http://www.w3cplus.com/blog/tags/431.html)属性,这是从SVG借来的另一个特性,现在它已经可以被应用于HTML元素了(至少在支持它的浏览器中),用好的、可读性强的语法,不像它的SVG副本,据说用起来容易让人抓狂。它的主要问题浏览器支持有限(我写这篇文章时)。但是,它的降级非常优雅(没有裁剪),所以这至少是可考虑的选择。
你可以在使用像Photoshop这样的图形编辑器,因为你对clipping path非常熟悉。Clipping path允许我们把元素裁剪成我们想要的形状。在这里,我们使用`polygon()`来指定一个菱形,这个形状允许我们使用一系列用逗号分隔的坐标点来指定任何多边形形状。
~~~
clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
~~~
就是它了!实现下图效果,相比之前需要两行HTML元素和八行隐蔽的CSS代码,现在只需要简单的一行。
![菱形图片](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e84d4ae14.png "菱形图片")
`clip-path`的令人惊叹之处不仅于此。这个属性是可添加动画的,只要是在两个相同的形状函数(这里是`polygon()`)之间,然后另一个形状有相同数量的坐标点。因此,如果我们想要在鼠标悬停的时候显示一幅完整的图像,我们可以这样写:
~~~
img {
clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
transition: 1s clip-path;
}
img:hover {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}
~~~
还有,这个方法可以很好地应用到非正方形的图像上,如下图所示。
![菱形图片](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e853b6dfd.png "菱形图片")
*图注:`clip-path`方法可以很好地应用于非正方形图像中*
啊~~现代CSS的乐趣~~
## 总结
在这一节中通过使用CSS的`transfrom`属性配合一定的数学计算,实现钻石图片效果。但这具有一定的局限性,对于正方形图片效果较好,但对于非正方形的图形效果就不太理想。实际上除了`transform`属性之外,还有更理想的方案,那就是使用`clip-path`对图片直接进行钻石形状的裁剪,从而实现我们需要的效果。
平行四边形
最后更新于:2022-04-01 03:43:49
> 原文出处:http://www.w3cplus.com/css3/css-secrets/parallelograms.html
## 问题
平行四边形是矩形的一个超集:它的边是平行的,但是角不一定是直角。
![平行四边形](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e0b5971a3.png "灵活的椭圆形")
*图注:一个平行四边形*
在视觉设计,它们往往可以使设计显得更具活力,传达运动感。
![平行四边形](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e0bb720ac.png "灵活的椭圆形")
*图注:eb设计中的平行四边形(design by Martina Pitakova)*
我们尝试用CSS创建按钮样式的链接。从普通的扁平按钮开始,带一些简单的样式,如图下图所示:
![平行四边形](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e0bc249ca.png "灵活的椭圆形")
然后,我们应用`skew()`变换创建出倾斜的矩形,如下:
~~~
transform: skewX(-45deg);
~~~
但是,这也会导致平行四边形中的内容被倾斜,这使得它看起来很丑而且没有可读性。
![平行四边形](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e0bc4916b.png "灵活的椭圆形")
*图注:倾斜后的按钮,文本变得难以阅读*
有什么办法可以只倾斜外边的形状容器,而不倾斜里边的内容吗?
## 嵌套元素的解决方案
我们可以给内容应用一个相反的`skew()`变换,把外边的变换抵消,这样就可以得到我们想要的结果。但是,这也意味着我们必须使用额外的HTML元素来包裹内容,如一个`div`:
~~~
<a href="#yolo" class="button">
<div>Click me</div>
</a>
.button { transform: skewX(-45deg); }
.button > div { transform: skewX(45deg); }
~~~
> 如果你是给默认内联的元素应用这个效果,记得把它的`display`属性设置为`inline-block`或`block`,否则应用的变换不会生效。内部元素也一样。
正如下图的效果:
![平行四边形](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e0bc72819.png "灵活的椭圆形")
如如你看到,它运行没有问题,但是我们使用了一个额外的HTML元素。如果修改标签不是一个可选的方案,或者你真的希望保持标签纯度,别担心,这里还有一个纯CSS的解决方案。
## 伪元素解决方案
另一个方案是把所有的样式应用在伪元素上(背景、边框等等),然后为其应用变换。因为我们的内容不是被包裹在伪元素中的,它不会被变换影响。我们尝试使用这种技术给一个链接添加样式,跟前一节一样的方法。
我们需要让伪元素保持灵活性,并自动继承父元素的尺寸,即使它们的大小由内容决定。一个简单的方法时是父元素应用`position: relative`,然后给生成的元素应用`position: absolute`,然后把所有的偏移量都设置为`0`,这样它的水平和垂直方向都会继承父元素的大小。代码如下:
~~~
.button {
position: relative;
/* text color, paddings, etc. */
}
.button::before {
content: '';
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
}
~~~
此时,生成的盒子是悬浮在内容上边的,一旦给它应用了背景,它将会覆盖住内容:
![平行四边形](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e0bcbb53e.png "灵活的椭圆形")
*图注:我们的伪元素当前是悬浮在内容上边的,所以应用`background: #58a`来覆盖*
我们可以给伪元素应用一个`z-index: -1`,这样它就会移动到父元素下方了。
现在,终于可以给我们的主要元素应用变换,然后查看结果了。最后的代码如下,生成的效果和前面的方案生成的效果完全一样:
~~~
.button {
position: relative;
/* text color, paddings, etc. */
}
.button::before {
content: ''; /* To generate the box */
position: absolute;
top: 0; right: 0; bottom: 0; left: 0;
z-index: -1;
background: #58a;
transform: skew(45deg);
}
~~~
这些技术不仅对`skew()`变换有效。它们还可以结合其它变换使用,这样既可以改变元素的形状,但又不会对它的内容造成影响。例如,给一个方形元素使用`rotate()`变换,可以很容易地生成一个钻石( 菱形)形状。
还有,使用伪元素和定位来生成一个盒子,并添加样式,然后放置在父元素之下的方法,在很多其它的不同类型的效果实例中都可以使用,如:
* 一种常见的解决IE8中多背景的方法,由[Nicolas Gallagher](http://nicolasgallagher.com/multiple-backgrounds-and-borders-with-css2/)提供。
* 可以解决像[第二章第四节中的“Inner rounding”这样的效果](http://www.w3cplus.com/css3/css-secrets/inner-rounding.html),能猜到是怎么实现的吗?
* 可以用来单独给背景应用像`opacity`这样的属性,由[Nicolas Gallagher](http://nicolasgallagher.com/css-background-image-hacks/)首创。
* 可以用来模拟多边框,以一种更灵活的方法。这样我们不可以使用[第二章第二节的“Multiple borders”的技术](http://www.w3cplus.com/css3/css-secrets/multiple-borders.html)。例如,当我们需要多个虚线边框,或多个用空格分隔的边框和透明度时。
灵活的椭圆形
最后更新于:2022-04-01 03:43:47
> 原文出处:http://www.w3cplus.com/css3/css-secrets/flexible-ellipses.html
## 问题
你可能注意过,在很多时候任何方形元素都可以直接应用一个非常大的`border-radius`来变成圆形,用类似下面这样的CSS代码:
~~~
background: #fb3;
width: 200px;
height: 200px;
border-radius: 100px; /* >= 边长的一半 */
~~~
![灵活的椭圆形](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e05bb3e06.png "灵活的椭圆形")
*图注:大小固定,`border-radius`的值为边长一半,对应生成的圆*
你也可以指定大于`100px`的`border-radius`,这同样会生成一个圆。原因请阅读规范:
> 当任何两个相邻边框半径之和超过了边框盒的尺寸,客户端必须按比例减小所有边框半径的值,直到它们相互之间没有重叠。 — [CSS Backgrounds & Borders Level 3](http://www.w3.org/TR/css3-background/#corner-overlap)
但是,我们的元素并不能总是指定固定的宽度和高度,因为我们希望它能够根据自己的内容自适应,而最后的元素大小是无法预知的。即使我们正在设计一个静态网站,而且它的内容是预先确定的,某个时候我们可能也想要进行修改;或者它可能会根据不同的度量显示成降级的字体。在这种情况下,如果它的宽度和高度不完全相等,我们通常希望它变成一个椭圆;如果相等,则为圆。但是,我们前面的代码却不是这样的。当宽度大于高度时,它的结果下图所示。
![灵活的椭圆形](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e06187f83.png "灵活的椭圆形")
*图注:前面的示例,当宽度大于高度时的情况;`border-radius`的圆在这里用虚线绘出*
我们可以用`border-radius`做一个椭圆,而且是灵活的椭圆吗?
## 解决方案
一个鲜为人知的内容是:`border-radius`接受水平和垂直方向不同值,使用斜杠(`/`)来分隔它们。这可以让我们在圆角处取整来创建椭圆。
![灵活的椭圆形](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e061b6aa1.png "灵活的椭圆形")
*图注:水平和垂直方向`border-radius`值不同的盒子;现在我们的圆角曲线变成了那个我们指定的`border-radius`值的椭圆,这里用虚线表示*
所以,如果我们有一个`200px × 150px`尺寸的元素,我们可以把它变成一个水平和垂直半径分别对应为其宽度和高度值一半的椭圆:
~~~
border-radius: 100px / 75px;
~~~
你看到结果如下图所示:
![灵活的椭圆形](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e066ecdc7.png "灵活的椭圆形")
*图注:应用不同的`border-radius`曲线创建的椭圆*
但是,这有一个非常大的缺陷:如果元素尺寸发生变化,`border-radius`的值也必须相应改变。你可以下图中看到`border-radius`的结果,如果我们把上边的半径应用给一个`200px × 300px`的元素。如果我们的元素尺寸依赖于内容,问题就出现了。
![灵活的椭圆形](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e06730894.png "灵活的椭圆形")
*图注:尺寸变化会破坏我们的椭圆;但是好的一点是,这种形状在一些圆柱中非常好用!*
`border-radius`的另一个鲜为人知的特性是它还接受百分比值,不只是长度值。百分比可以解析为相应的尺寸,水平半径的宽度和垂直半径的高度。这意味着相同的百分比可以计算出不同的水平半径和垂直半径。因此,为了创建灵活椭圆,我们可以给半径应用`50%`:
~~~
border-radius: 50% / 50%;
~~~
因为斜杠前后的值是一样的(尽管它们计算出的是不同的值),我们可以把它进一步简化为:
~~~
border-radius: 50%;
~~~
结果就是只用一行CSS就可以创建出灵活的椭圆,不必考虑元素的宽度和高度。
### 为什么要使用`border-radius`?
很多人好奇`border-radius`为什么叫这个名字,因为它的使用不涉及到边框。叫`corner-radius`还更合适一些。这样叫的原因是`border-radius`包裹了元素的边框盒的边缘。如果元素没有边框的话,就没有任何区别了,但是如果有边框,它就是边框的外圆角。内圆角的近似值较小(`max(0, border-radius` - `border-width)`比较精确)。
## 半椭圆
现在我们已经知道如何用CSS创建一个灵活的椭圆,自然而然我们就会想到我们是否可以创建其它常见的形状,比如椭圆的一部分。我们可以花点时间来看看如何创建一个半椭圆。
![灵活的椭圆形](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e067843f9.png "灵活的椭圆形")
*图注:一个半椭圆*
> 一个半椭圆在宽度等于高度两倍的时候,可以变成一个半圆(或当高度是宽度的两倍,椭圆沿着横轴剪断)。
它是相对于纵轴对称的,但不相对于横轴对称。即使我们还无法知道确切的`border-radius`的值(它可以是所有可能的值),对于每个角我们需要不同的半径值这点是很明确的。但是,我们目前尝试过的是四个角都用同一个半径值。
幸好,`border-radius`的语法比较灵活。你可能会很惊讶`border-radius`竟然是一个简写!!我们可以为每个角提供不同的值,有两个方法可以来完成。一种方法是使用普通写法来组成:
* `border-top-left-radius`
* `border-top-right-radius`
* `border-bottom-right-radius`
* `border-bottom-left-radius`
但是,比较简洁的方式是直接使用`border-radius`简写,应用多组值,用空格分隔。如果我们提供四个值,分别应用于对应的角,从左上角开始,顺时针旋转。如果我们提供少于四个值,它们以通常的CSS方式相乘,类似于`border-width`这样的属性。三个值的情况表示第四个值和第二个值相等,两个值的情况表示第三个值和第一个值相等。
![灵活的椭圆形](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e067b7748.png "灵活的椭圆形")
*图注:分别指定了`4`,`3`,`2`或`1`个值的`border-radius`属性(用空格分隔)对应的图。(注意椭圆的半径,斜杠前后都可以达到四个值,它们指的都是相同的角,斜杠前的是水平半径,斜杠后的是垂直半径)。*
上图为这个原理提供了一个可视化的解释。我们甚至可以为四个角分别应用不同的水平半径和垂直半径,通过在斜杠前后分别应用`1-4`个值。需要注意的是,这些可以分别扩展成四个值。例如,`10px / 5px 20px`的`border-radius`值,等于`10px 10px 10px 10px / 5px 20px 5px 20px`。
有了这个新东西之后,我们现在再来看看半椭圆的问题。指定这样的`border-radius`的值是否可以生成像这样的形状呢?不去尝试我们也不会知道结果。开始做之前我们先分析一下:
* 形状在水平方向是对称的,这也就意味着左上角和右上角的半径是一样的;同样的,左下角和右下角的半径也是一样的。
* 顶部没有水平边缘(如,整个顶部是曲线),也就是说左上角和右上角的半径应该都是该形状宽度的`100%`。
* 从前面的两条分析,我们可以推断,顶角水平方向的左右半径应该是`50%`。
* 垂直方向,顶部的两个角都应用了整个元素的高度,底部的角没有圆角。因此,垂直方向的`border-radius`的值应该是`100% 100% 0 0`。
* 因为底部角的垂直圆角是0,水平方向的圆角是多少都没有关系了,因为它们最后计算出的结果都会是`0`。(你可以想象一个圆角的垂直半径为`0`,而水平半径为正值吗?即使是规范的编辑也没办法做好。)
综上所述,我们可以很简单地写出灵活半椭圆的CSS代码,下图那样的:
~~~
border-radius: 50% / 100% 100% 0 0;
~~~
![灵活的椭圆形](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e067843f9.png "灵活的椭圆形")
创建一个沿着纵轴截断的椭圆也非常容易:
~~~
border-radius: 100% 0 0 100% / 50%;
~~~
效果如下图所示:
![灵活的椭圆形](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e06866bb2.png "灵活的椭圆形")
*图注:沿着纵轴截断的半椭圆*
作为一个练习,试试写一下另一半椭圆的CSS代码。
## 四分之一椭圆
在创建了椭圆和半椭圆之后,下一个问题自然是我们是否可以创建一个四分之一椭圆,像下图所示:
![灵活的椭圆形](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e068a5278.png "灵活的椭圆形")
*图注:四分之一椭圆*
按照之前类似的思考过程,我们注意到,要创建一个四分之一椭圆,其中一个角需要有一个`100%`半径,水平和垂直方向都是,而其它四个则没有圆角。因为四个角的水平和垂直半径的百分比都是一样的,就不需要应用斜杠了。代码如下所示:
~~~
border-radius: 100% 0 0 0;
~~~
> 和半椭圆的示例相似,当元素的宽度和高度相等时,它就变成四分之一圆了。
但是,你现在可能好奇椭圆的其它部分用`border-radius`是否可以完成(如,八分之一椭圆是否可以?三分之一?),我想你可能会失望了,因为没有可能的`border-radius`值可以生成这样的形状。
[![灵活的椭圆形](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e069db9c3.png "灵活的椭圆形")](http://simurai.com/archive/buttons)
Simurai熟练并且充分地使用`border-radius`来为它的[BonBon按钮](http://simurai.com/archive/buttons/)创建各种形状。
## 总结
[`border-radius`](http://www.w3cplus.com/node/48)可以给一个元素制作圆角。险些之外,还可以使用它制作随圆形。而这篇文章中主要阐述了如何使用`border-radius`制作除圆之外的图形——**椭圆形**。
图片边框
最后更新于:2022-04-01 03:43:44
> 原文出处:http://www.w3cplus.com/css3/css-secrets/continuous-image-borders.html
## 问题
有时候,我们想将一些图案或图片添加为边框,而不是背景。比如,在下图中所示的元素就拥有一个装饰性的边框,边框内是一个被限制在边框区域的图片。此外,我们还希望图片可以覆盖整个边框区域。那么我们怎样使用 CSS 达到这一目的呢?
![图片边框](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cddee01bf9.png "图片边框")
*图注:图片被用来装饰各种高度的边框*
此时,也许在你的内心会响起一个响亮的声音:“border-image,border-image,我们可以使用 `border-image`,它可以完美的解决这个问题!”年轻人,做事不要太急躁。让我们重新回忆一下 `border-image` 的解析机制:它实际上将背景分成了九块,然后分别将各个块作用到了元素的边角上。下图就是一个视觉原理图。
![图片边框](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cddee511d4.png "图片边框")
*图注:[`border-image`](http://www.w3cplus.com/content/css3-border-image) 的快速入门。上面:分割后的图片;虚线框内的就是分割后的碎片;中间:`border-image: 33.34% url(...) stretch;` 下面: `border-image: 33.34% url(...) round;`,[在线示例链接](http://codepen.io/airen/pen/qOqGrL)*
那我们是否可以将图片切分成块,然后使用 `border-image` 模拟出下图这样的效果呢?
![图片边框](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cddee01bf9.png "图片边框")
实际上,即使我们精确计算出了元素的尺寸和边框宽度,仍然无法达到自动适配多种尺寸的目的。问题的核心就是,我们并不是让边角具有某种特定的图形,而且即使添加上了图形,当元素尺寸变化时,图形也会发生形变。如果你稍微尝试一下,就会理解使用`border-image` 是无法实现效果的,那么我们应该怎么办呢?
解决这个问题最简单的方法就是使用两个 HTML 标签:一个标签使用目标图片作为北京,两一个使用白色作为背景并覆盖在前一个标签上面:
~~~
<div class="something-meaningful">
<div>
I have a nice stone art border,
don’t I look pretty?
</div>
</div>
.something-meaningful {
background: url(stone-art.jpg);
background-size: cover;
padding: 1em;
}
.something-meaningful > div {
background: white;
padding: 1em;
}
~~~
这种方法确实实现了下图的效果:
![图片边框](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cddee01bf9.png "图片边框")
但是它却需要添加额外的 HTML 标签。这只能算是一种折中的办法:它不仅仅混合了表现和样式,而是在某些情况下必须修改 HTML 结构。那么我们是否可以只用一层标签就实现这个效果呢?
## 解决方案
值得庆幸的是,CSS 的渐变以及在 [Backgrounds & Borders Level 3](http://w3.org/TR/css3-background) 中扩展后的 `background` 属性可以帮助我们实现这一个效果,而且是只用一个标签。这种方法的核心就是使用纯色覆盖背景图片。不过,为了让第二章图片装饰到边框上,我们需要为[`backgroud-clip`](http://www.w3cplus.com/content/css3-background-clip) 属性配置不同的属性值。最后一件事就是让最底下的图层只有一种颜色,所以我们需要通过 CSS 渐变模拟一个纯白色背景。
我们最初实现这个方法的代码就像下面一样:
~~~
padding: 1em;
border: 1em solid transparent;
background: linear-gradient(white, white),
url(stone-art.jpg);
background-size: cover;
background-clip: padding-box, border-box;
~~~
![图片边框](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cddef484f3.png "图片边框")
*图注:第一次的尝试非常接近理想效果。*
正如上图所示,效果非常接近我们的目标了,唯一的不足是多了一些重复的部分。其中的原因就是 [`backgroud-origin`](http://www.w3cplus.com/content/css-background-origin) 属性的默认值为 `padding-box`,因此,图片会定位到内边距盒模型的左上角,剩余的部分就会不断平铺这一图片。为了矫正这一效果,我们需要将 `background-origin` 的属性值修改为 `border-box`:
~~~
padding: 1em;
border: 1em solid transparent;
background: linear-gradient(white, white),
url(stone-art.jpg);
background-size: cover;
background-clip: padding-box, border-box;
background-origin: border-box;
~~~
这些新属性也可以统一使用 `background` 简写语法来声明,从而让我们的代码更加简洁优雅:
~~~
padding: 1em;
border: 1em solid transparent;
background:
linear-gradient(white, white) padding-box,
url(stone-art.jpg) border-box 0 / cover;
~~~
当然,我们也可以将这一技巧应用到和渐变相关的背景图案上。比如,看一看下面的这些代码,我们可以用它来生成信封风格的边框:
~~~
padding: 1em;
border: 1em solid transparent;
background: linear-gradient(white, white) padding-box,
repeating-linear-gradient(-45deg,
red 0, red 12.5%,
transparent 0, transparent 25%,
#58a 0, #58a 37.5%,
transparent 0, transparent 50%)
0 / 5em 5em;
~~~
最终结果如图所示:
![图片边框](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cddef80d32.png "图片边框")
*图注:我们实现的怀旧信封边框*
现在,你可以使用 `background-size` 随意修改条纹的宽度,也可以使用 `border` 随意修改边框的宽度。不同于前一个示例,这种效果是可以使用 `border-image` 来实现的:
~~~
padding: 1em;
border: 16px solid transparent;
border-image: 16 repeating-linear-gradient(-45deg,
red 0, red 1em,
transparent 0, transparent 2em,
#58a 0, #58a 3em,
transparent 0, transparent 4em);
~~~
不过,使用 `border-image` 会遇到几个问题:
* 每次修改 `border-width` 的时候,同时需要修改 `border-image-slice`
* 因为不能在 `border-image-slice` 中使用类似 `em` 的单位,所以我们只能对边框宽度使用像素单位
* 条纹的宽度需要硬编码到颜色过渡点上,那么当需求变化时就需要修改四个地方的代码
这种技巧的另一种有趣应用就是创建一个类似选区的边框(marching ants borders)。选区型边框(译者:之所以称之为选区型,是因为效果非常类似 ps 中选区边框的效果,更容易理解)是虚线边框,而且虚线是实时滚动的,非常类似行进中的蚂蚁(当然,这需要一定的想象力将虚线想象为蚂蚁)。这在 GUI 中几乎是不可想象的效果。图像编辑软件通常使用这种边框来表示这是一块被选中的区域,简称选区。
![图片边框](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cddefbdfe1.png "图片边框")
*图注:选区型边框在 Adobe Photoshop 中被用来标志被选中的地方*
为了创建这种边框,我们将要使用前面创建信封边框的技巧。首先是将条纹更改为黑白两色,并且将边框调整为 `1px` 的宽度(你注意到现在条纹是怎样变化为虚线边框的了吧?),然后适当修改一个 `background-size`,最后为 `background-size` 添加动画效果(从初始值增加到 `100%`):
~~~
@keyframes ants { to { background-position: 100% } }
.marching-ants {
padding: 1em;
border: 1px solid transparent;
background:
linear-gradient(white, white) padding-box,
repeating-linear-gradient(-45deg,
black 0, black 25%, white 0, white 50%
) 0 / .6em .6em;
animation: ants 12s linear infinite;
}
~~~
现在,你可以从下图中看到一个静态的效果。
![图片边框](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cddf008859.png "图片边框")
*图注:在纸上显示选区型边框是不可能的,可以访问在线示例进行查看——非常有意思!*
显然,这种技巧不仅仅对选区型边框有用,只需修改一下边框颜色和虚线之间的间距,就可以用来创建各种自定义的虚线边框。
目前,如果想要使用 `border-image` 属性实现类似的效果,那么就只能使用 `border-image-source` 添加 GIF 来模拟了,详情见 [Marching ants animated selection rectangle in CSS](http://www.chrisdanford.com/blog/2014/04/28/marching-ants-animated-selection-rectangle-in-css/)。当浏览器广泛支持渐变效果之后,我们就可以用渐变来创建这种效果了,而且代码会更加简练优雅。
![图片边框](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cddf03c36f.png "图片边框")
*图注:顶部剪切过得边框,用来模拟传统书籍中的脚注*
不过,`border-image` 也是非常强大的属性,甚至有时比渐变更强大。比如,假设我们需要一个裁剪过的顶部边框,就像常见的脚注样式。那么只需要使用 `border-image`和垂直渐变就可以实现,其中裁剪的长度可以硬编码实现。边框的宽度则有 `border-width` 来控制。代码就像是这样:
~~~
border-top: .2em solid transparent;
border-image: 100% 0 0 linear-gradient( 90deg,
padding-top: 1em;
currentColor 4em,
transparent 0);
~~~
最终结果如上图所示。此外,因为我们在这里使用 `em` 单位,所以整体效果就能随字体大小而变化;因为使用了`currentColor`,所以它也可以随字体颜色而改变颜色。
## 总结
在CSS2.1的时代要实现图片边框效果是一种奢侈的想法,但在CSS3中虽然增加了[`border-image`](http://www.w3cplus.com/content/css3-border-image)属性实现图片边框,但依旧受到诸多的限制性,其中最大的限制就是浏览器对其支持度。在这一节中,我们介绍了使用多背景,以及配合[`background-clip`](http://www.w3cplus.com/content/css3-background-clip)、[`background-origin`](http://www.w3cplus.com/content/css-background-origin)属性模拟出图片边框效果。
随机背景
最后更新于:2022-04-01 03:43:42
> 原文出处:http://www.w3cplus.com/css3/css-secrets/random-backgrounds.html
## 问题
虽然平铺几何图形很棒,但看起来还是有点乏味。**而在自然界中,万事万物皆有不同。**即使是在相似中,也充满了变化和随机因素。你可以看一看花圃:虽然花朵具有一致的艳丽,但也各有各的亮点。这个世界上没有任何两朵花是相同的。这就是为什么我们要尝试让背景图案接近真实,而且我们也在尝试让平铺的元素之间尽量减少缝隙,让它们显得浑然天成自成一体,但是这也会让样式文件的大小超出我们的预期。
> 当你发现了某些独特的特性——比如木头纹理上特定距离就会出现的结,就会打破随机性这种感觉 — Alex Walker, [The Cicada Principle and Why It Matters to Web Designers](http://sitepoint.com/the-cicada-principle-and-why-it-matters-to-web-designers)
模拟随机是一件非常具有挑战性的事情,因为 CSS 并没有任何创造随机性的能力。让我们重新拿起条纹这个示例,假设我们想要得到拥有各种颜色和宽度的垂直条纹(让我们简化为四种颜色吧),并且还要让它们之间过渡自然。我们的第一个想法可能就是创建一个渐变:
~~~
background: linear-gradient(90deg,
#fb3 15%, #655 0, #655 40%,
#ab4 0, #ab4 65%, hsl(20, 40%, 90%) 0);
background-size: 80px 100%;
~~~
就像下图看到的那样
![随机背景图](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e8ee17650.png "随机背景图")
*图注:实现伪随机条纹的初次尝试,其中所有的颜色都是由相同的线性渐变生成的*
非常明显就分辨出了重复元素,这主要是因为它只重复了 `80px`(`background-size`)。我们可以做得更好吗?
## 解决方案
对于这一问题,我的第一个想法就是将整个的条纹分成不同块,然后增加这些块的随机性:一个基色和三个条纹层,在不同的距离上实现平铺。通过硬编码颜色过渡点的位置,我们可以改变条纹的宽度,通过使用 `backgroud-size`,我们可以控制间距,最终就能实现上面的初步设想了:
~~~
background: hsl(20, 40%, 90%);
background-image:
linear-gradient(90deg, #fb3 10px, transparent 0),
linear-gradient(90deg, #ab4 20px, transparent 0),
linear-gradient(90deg, #655 20px, transparent 0);
background-size: 80px 100%, 60px 100%, 40px 100%;
~~~
因为最顶层的平铺将会最容易被注意到(因为它覆盖了所有的图层),所以我们对最上面的一层施加最大的间歇性间距(在本例中,就是桔黄色条纹)。
![随机背景图](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e8f37164a.png "随机背景图")
*图注:第二次尝试,通过不同的 `background-size` 让不同的图层发生重叠;虚线框内的图案是用来平铺的图案*
正如上图中所看到的那样,整体看起来随机多了,但如果我们仔细观察,还是能意识到每 `240px` 就会有一次重复。也就是说,在此类组合中,所有重复块的偏移量就是第一个非重复条纹终点位置的一倍或数倍。也许你还记得在学校里我们学过的最小公倍数(LCM,Least Common Multiple),它是一个整数集合中所有整数的一倍或多倍。因此,这里条纹块的大小就是背景大小的最小公倍数,即 `40/60/80`,它们的最小公倍数就是 `240`。
上面的示例就遵循了这一不易察觉的随机性,所以我们需要增大平铺条纹块的大小。感谢数学的存在,无需漫长和痛苦的计算我们就可以实现它,因为我们已经知道了答案。为了达到最大的最小公倍数,那么这些数之间必须互质。在本例中,最小公倍数就是由它们得出来的。比如,`3/4/5` 就互为质数,所以它们的最小公倍数就是 `3 × 4 × 5 = 60`。让数值互为质数的最简单方式就是让它们是质数,在数学上质数和其他任何数都互为质数。质数的列表在网络上随处可见。
为了保持最大的随机性,我们甚至使用质数来设计条纹的宽度。这就是我们的代码:
~~~
background: hsl(20, 40%, 90%);
background-image:
linear-gradient(90deg, #fb3 11px, transparent 0),
linear-gradient(90deg, #ab4 23px, transparent 0),
linear-gradient(90deg, #655 41px, transparent 0);
background-size: 41px 100%, 61px 100%, 83px 100%;
~~~
是的,代码很难看,但是效果如下图所示:
![随机背景图](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-05_5611e8f3b007d.png "随机背景图")
*图注:最终的条纹,使用质数来增加随机性*
现在我们的条纹至少 `41 × 61 × 83 = 207583px` 才会平铺一次,完全超出了一个屏幕分辨率所能容纳的数量。
上面的技巧(使用质数增加背景平铺时的随机性)被称为 “The Cicada Principle”,由 Alex Walker 第一次提出。值得注意的是,这不仅仅对背景有用,而且对需要平铺的任何事情都有用。其他的应用包括但不限于:
* 为一个图库中图片的旋转角度增加一点点随机性,可以使用 `:nth-child(an)` 选择器,其中 `a` 为质数。
* 为动画的运动过程添加一些随机性,那么可以添加多个以质数为时间长度的持续时间(点击这里查看[示例](http://codepen.io/airen/pen/WQoBRR))
> 向提出这一技巧的 Alex Walker 致敬, [The Cicada Principle and Why It Matters to Web Designers](http://www.sitepoint.com/the-cicada-principle-and-why-it-matters-to-web-designers/)。 [Eric Meyer](http://meyerweb.com/) 随后提出了 [Cicadients](http://meyerweb.com/eric/thoughts/2012/06/22/cicadients/) 这一概念, 其中就包含了将该技巧通过 CSS 渐变添加到背景图片上。Dudley Storey 也就该概念发表了[大量非常有价值的文章](http://demosthenes.info/blog/840/Brood-X-Visualizing-The-Cicada-Principle-In-CSS).
## 总结
前面介绍的内容,不管是使用渐变实现的线性渐变还是径向渐变,或者说是复杂的纹理背景,都是具有一定的规律性。在这一节中主要向大家阐述了如何实现随机性的背景图效果。
复杂背景图案
最后更新于:2022-04-01 03:43:40
> 原文出处:http://www.w3cplus.com/css3/css-secrets/complex-background-pattern.html
## 问题
在[上一节中](http://www.w3cplus.com/css3/css-secrets/striped-backgrounds.html),我们学习了如何使用 CSS 渐变创建各种类型的条纹。不过,条纹并不能代表所有的背景图案,最多只能算是背景中的几何图案。此外,我们还会常常用到许多类型的背景,比如网格、波尔卡圆点、棋盘格等等。
值得庆幸的是,CSS 的渐变属性就可以帮我们创建这些背景图案。虽然几乎可以使用 CSS 的渐变创建各种类型的几何图案,但是这些背景图案的效果并不具有实用性。如果开发中稍不注意,那么代码就会迅速变得冗杂和繁琐。CSS 的背景图案也是非常适合使用 CSS 预处理器来处理的问题之一,比如可以使用 [Sass](http://www.w3cplus.com/blog/tags/302.html) 来减少重复背景图案中的重复工作。
![复杂背景图案](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdd8e37903.png "复杂背景图案")
> 在 2011 年的时候,我的 [CSS3 图案集](http://lea.verou.me/css3patterns/) 就展示了大量的 CSS 渐变图案。之后在 2011 年和 2012 年期间,有大量和 CSS 渐变相关的文章、书籍和会议引用了它们,甚至会被浏览器厂商拿来调整浏览器在渐变方面的功能。不过,并不是每一个模式都是可以用于线上产品的,有一些只是用来演示 CSS 渐变的能力,但是具体的代码往往过于冗杂。对于这些情况,SVG 显然是个更好的选择。如果你想了解更多的 SVG 信息,可以查看 [SVG Patterns](http://philbit.com/svgpatterns/),该网站展示了大量 SVG 版本的 CSS 图案集。
在本章中,我们将会着重创建简单且实用的图案。
## 网格
当我们只能使用一次渐变时,往往就限制了背景图案的种类。下面将要展示给各位的魔幻效果就是要组合多个渐变,让它们交叉重叠。最简单的图案就是使用交叉重叠的水平和垂直条纹创建各种类型的网格。比如,下面的代码就是用来创建怀旧风格的桌布图案:
![复杂背景图案](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdd8fcd69d.png "复杂背景图案")
*图注:由两个渐变组成的桌布图案(这里的透明色就是传统的灰色棋盘格)*
~~~
background: white;
background-image: linear-gradient(90deg,
rgba(200,0,0,.5) 50%, transparent 0),
linear-gradient(
rgba(200,0,0,.5) 50%, transparent 0);
background-size: 30px 30px;
~~~
在某些情况下,我们会想要调整网格中单元格内部的尺寸,而又不影响单元格整体的大小。这也是一种很好地情境,让我们使用固定长度代替百分比来设置过渡点的位置:
~~~
background: #58a;
background-image:
linear-gradient(white 1px, transparent 0),
linear-gradient(90deg, white 1px, transparent 0);
background-size: 30px 30px;
~~~
效果如图所示
![复杂背景图案](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdd900e8e4.png "复杂背景图案")
*图注:一个基础的蓝图网格图案,每个单元格的边框为`1px`,边框的宽度和整个单元格的宽度无关*
上图是一个具有 `1px` 白线的网格,网格中的每个单元格保持了 `30px` 的大小。就像在上一节的 “[灵活可扩展的精致斜纹](http://www.w3cplus.com/css3/css-secrets/striped-backgrounds.html)” 部分所说,最初设定的背景色也是一种降级处理的技巧。
总体来看,这个网格图案是高可维护性代码(虽然其中仍然有一些重复代码)的典范:
* 当需求变更时,可以迅速定位到需要更改的网格尺寸、宽度和颜色
* 需改样式时无需过多编辑,只需一两处修改即可实现
* 短小简洁,只需要四行代码,大约 `170b`的代码量,如果使用 SVG 的话是远远无法如此精简的
> 将 CSS 图案的文件黏贴到 [bytesizematters.com](http://bytesizematters.com/) 这个网站,就可以计算出文件大小
我们甚至可以交叉多层不同宽度和颜色的网格来创建一个更真实的网格(类似蓝图图纸,如下图):
![复杂背景图案](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdd954f756.png "复杂背景图案")
*图注:更复杂的蓝图图案,主要由两个网格组成,每个网格都有不同的样式*
~~~
background: #58a;
background-image:
linear-gradient(white 2px, transparent 0),
linear-gradient(90deg, white 2px, transparent 0),
linear-gradient(hsla(0,0%,100%,.3) 1px,
transparent 0),
linear-gradient(90deg, hsla(0,0%,100%,.3) 1px,
transparent 0);
background-size: 75px 75px, 75px 75px,
15px 15px, 15px 15px;
~~~
## 波尔卡圆点
到目前为止,我们只是使用了线性渐变来创建背景图案。在此之外,径向渐变通常非常有用,它可以帮助我们创建圆形、椭圆形,或者是这些图形的部分。使用径向渐变创建的最简单图案就是大量的圆点,如下图:
![复杂背景图案](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdd95b4752.png "复杂背景图案")
*图注:一个点集;虚线框内是用来平铺的图案*
~~~
background: #655;
background-image: radial-gradient(tan 30%, transparent 0);
background-size: 30px 30px;
~~~
诚如所见,这种图案往往用处不大。不过,接下来我们可以使用两个径向渐变的组合,通过设定不同的 `background-size` 来创建类似波尔卡圆点的图案:
![复杂背景图案](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdd9b1175f.png "复杂背景图案")
*图注:波尔卡圆点图案;虚线框内的两个图案都是是用来平铺的图案*
~~~
background: #655;
background-image: radial-gradient(tan 30%, transparent 0),
radial-gradient(tan 30%, transparent 0);
background-size: 30px 30px;
background-position: 0 0, 15px 15px;
~~~
值得注意的是,为了实现这种效果,就必须让第二个渐变的 `background-position` 为 `background-size` 的一半。不幸的是,这意味着当我们需要修改背景大小时,就至少只需在四个地方进行修改。虽然无法定论这样的代码已经混乱了,但可以确信的说,我们的代码已经接近混乱边缘了。如果你正在使用预处理器,那么你就可以将它转换成一个混合宏:
~~~
@mixin polka($size, $dot, $base, $accent) {
background: $base;
background-image:
radial-gradient($accent $dot, transparent 0),
radial-gradient($accent $dot, transparent 0);
background-size: $size $size;
background-position: 0 0, $size/2 $size/2;
}
~~~
现在,当我们再次创建波尔卡圆点时,只需像下面这样做就可以了:
~~~
@include polka(30px, 30%, #655, tan);
~~~
## 棋盘格
棋盘格图案在现实世界中有大量的应用。比如,轻质的棋盘格可以用来替换古板的纯色背景,让页面显得更有趣。此外,在众多的 UI 设计中,灰色的棋盘格背景已经被广泛地理解为了透明效果。使用 CSS 创建一个棋盘格图案是可能的,但是其中的艰辛将会超出我们的想象。
![复杂背景图案](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdda04c6a3.png "复杂背景图案")
*图注:灰色棋盘格图案往往用来表示透明区域;如果使用图片平铺的方式实现这种效果,那么虚线框内的图案就是要平铺的图案*
正如上图所示,一个典型的棋盘格格子,有两个不同颜色的正方形不断重复组成的。乍看之下,好像很容易就可以创建出来:只需要创建两个位置不同的矩形嘛。这样做对吗?显然并没有那么简单。虽然我们可以使用 CSS 渐变创建矩形,但是如果矩形周围没有可用空间的话,最终的效果就会变成一个纯色背景。实际上,我们是无法使用 CSS 渐变创建具有额外空间的矩形。如果你对此有所疑问,可以动手试一试,当你让渐变平铺时,就会生成类似下图的效果。
![复杂背景图案](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdda082e00.png "复杂背景图案")
*图注:平铺一个四周有空白的正方形;虚线框内是用于平铺的图案*
实现这种效果的技巧就是,使用两个直角三角形拼接成正方形。在前面的集结中,我们已经知道了如何创建直角三角形。如下图所示:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdda0b34a6.png)
为了让你回忆起来,你可以看看下面的代码:
~~~
background: #eee;
background-image:
linear-gradient(45deg, #bbb 50%, transparent 0);
background-size: 30px 30px;
~~~
这个时候你可能疑惑了,这样做有什么用吗。当然有用,如果我们想合成类似下图效果:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdda0b34a6.png)
那样的正方形,我们就会得到一个纯色。不过,如果我们将三角形的长度减半,那么它们就会占有原来空间的八分之一,而不是现在的二分之一,这种情况下又会是怎样的效果呢?我们只需将颜色过渡点的位置从 `50%` 调整为 `25%`,即可完成这一调整。最后的效果就是下图这样的直角三角形了。
![复杂背景图案](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdda11536c.png "复杂背景图案")
*图注:周围有大量空白的直接三角形*
与上面道理相同,只需翻转颜色过渡点即可创建反方向的直接三角形:
![复杂背景图案](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdda13a0ff.png "复杂背景图案")
*图注:翻转颜色过渡点之后,得到相反方向的三角形*
~~~
background: #eee;
background-image:
linear-gradient(45deg, transparent 75%, #bbb 0);
background-size: 30px 30px;
~~~
> 展望:锥形渐变(Conical gradients) 未来,我们创建棋盘格背景将不再需要费心地交叉覆盖三角形。在 [CSS Image Values Level 4](http://w3.org/TR/css4-images) 中,定义了一系列新的渐变函数来生成锥形渐变(又名,“angle gradients”)。这些渐变从俯视的角度来看就像一个圆锥,所以得名锥形渐变。锥形渐变由一系列的颜色过渡点组成,并且从一个固定点开始旋转变化。比如,使用下面的代码就可以创建一个色调取色盘: ```background: conic-gradient(red, yellow, lime, aqua, blue, fuchsia, red); `` 锥形渐变在很多情况下都非常有用,比如星群、金属拉丝等效果,当然也包括棋盘格效果。未来,创建一个类似棋盘格只需使用一个渐变就可以实现:``` background: repeating-conic-gradient(#bbb 0, #bbb 25%, #eee 0, #eee 50%); background-size: 30px 30px; ``` 不幸的是,本书写作的时候的还没有任何浏览器实现了这一渐变。
如果我们创建两个渐变,猜猜看会有什么结果呢?
~~~
background: #eee;
background-image:
linear-gradient(45deg, #bbb 25%, transparent 0),
linear-gradient(45deg, transparent 75%, #bbb 0);
background-size: 30px 30px;
~~~
首先,结果就像下图所示一般,它并不像我们所要实现的效果。不过,我们只需要将第二个渐变的位置移动 `background-size`的一半,就可以将它们合并成正方形了:
![复杂背景图案](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdda17f4de.png "复杂背景图案")
*图注:合并两个三角形图案*
~~~
background: #eee;
background-image:
linear-gradient(45deg, #bbb 25%, transparent 0),
linear-gradient(45deg, transparent 75%, #bbb 0);
background-position: 0 0, 15px 15px;
background-size: 30px 30px;
~~~
你能猜到最终的结果会是什么样子吗?它就是我们想要实现的棋盘格:
![复杂背景图案](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdda1b5231.png "复杂背景图案")
*图注:三角形合并后变成了正方形,并且在周围保持了空白区域;虚线框内的两个图案是用于平铺的图案,第二个渐变用深色标志*
请注意,这只是棋盘格的一半。如果想创建一个完整的正方形棋盘格,只需重复一下上面的两条渐变,然后调整一下位置即可,有点类似创建波尔卡圆点的操作:
~~~
background: #eee;
background-image:
linear-gradient(45deg, #bbb 25%, transparent 0),
linear-gradient(45deg, transparent 75%, #bbb 0),
linear-gradient(45deg, #bbb 25%, transparent 0),
linear-gradient(45deg, transparent 75%, #bbb 0);
background-position: 0 0, 15px 15px,
15px 15px, 30px 30px;
background-size: 30px 30px;
~~~
最终的结果就是和下图所示的棋盘格了。
![复杂背景图案](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdda04c6a3.png "复杂背景图案")
此外,通过合并对立的三角形(比如,第一个对第二个,第三个对第四个)、添加暗色半透明的基色,可以提高代码的可维护性,无需调整顶层的颜色,只需更改基色即可应对新的需求:
~~~
background: #eee;
background-image:
linear-gradient(45deg,
rgba(0,0,0,.25) 25%, transparent 0,
transparent 75%, rgba(0,0,0,.25) 0),
linear-gradient(45deg,
rgba(0,0,0,.25) 25%, transparent 0,
transparent 75%, rgba(0,0,0,.25) 0);
background-position: 0 0, 15px 15px;
background-size: 30px 30px;
~~~
现在,我们用两个渐变替换了四个渐变,代码总体简练多了。如果需要改变颜色或者单元格大小,则至少需要修改四处地方。在这一个问题上,使用预处理器的混合宏显然是一个明智之举。比如,可以在 Sass 中这样处理:
~~~
@mixin checkerboard($size, $base, $accent: rgba(0,0,0,.25) {
background: $base;
background-image: linear-gradient(45deg,
$accent 25%, transparent 0,
transparent 75%, $accent 0),
linear-gradient(45deg,
$accent 25%, transparent 0,
transparent 75%, $accent 0);
background-position: 0 0, $size $size,
background-size: 2*$size 2*$size;
}
/* Used like... */
@include checkerboard(15px, #58a, tan);
~~~
在任何情况下,当我们需要这么代码解决问题的时候,那么使用 SVG 将会更合适。使用 SVG 格子的效果的话,代码就会变得很简单:
~~~
<svg xmlns="http://www.w3.org/2000/svg"
width="100" height="100" fill-opacity=".25" >
<rect x="50" width="50" height="50" />
<rect y="50" width="50" height="50" />
</svg>
~~~
有人可能会质疑说:“如果使用 CSS 的话,就可以节省 HTTP 请求了!”不过,在现代浏览器中,我们可以将 SVG 文件嵌入到样式中,作为一个数据 URI,我们甚至不需要使用 base64 和 URLencode:
~~~
background: #eee url('data:image/svg+xml,\
<svg xmlns="http://www.w3.org/2000/svg" \
width="100" height="100"
fill-opacity=".25">\
<rect x="50" width="50" height="50" /> \
<rect y="50" width="50" height="50" /> \
</svg>');
background-size: 30px 30px;
~~~
SVG 版本的棋盘格不仅仅是小到了只有 40 个字符,更棒的是减少了重复性的工作。比如,我们只需修改一处地方,就可以更改颜色,只需修改两处地方,就可以更改大小。
**注意,你可以通过使用反斜线(`\`)将 CSS 字符串分解到多行,从而提高可读性。**
## 相关链接
* [CSS Image Values](http://www.w3cplus.com/css3/css-secrets/w3.org/TR/css-images)
* [CSS Backgrounds & Borders](http://www.w3cplus.com/css3/css-secrets/w3.org/TR/css-backgrounds)
* [Scalable Vector](Graphics w3.org/TR/SVG)
* [CSS Image Values Level 4](http://www.w3cplus.com/css3/css-secrets/w3.org/TR/css4-images)
可以通过 [叠加模式](http://w3.org/TR/compositing-1)(也被称为[CSS混合模式](http://www.w3cplus.com/blog/tags/409.html)) 来组合这些技巧,使用 `background-blend-mode` 的时候,使用除 `normal` 之外的其他属性值作用到一个或多个图层,会产生非常有趣的效果,就像是 [bennettfeely.com的gradients示例](http://bennettfeely.com/gradients/) 里所展示的一样。这些图案大多只使用了 `multiply` 的叠加模式,在此之外`overlay`,`screen` 或者是 `difference` 都非常有用。
![复杂背景图案](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdda7330ac.png "复杂背景图案")
## 总结
[上一节](http://www.w3cplus.com/css3/css-secrets/striped-backgrounds.html)主要介绍了使用CSS3的渐变配合`background-size`制作简单的条纹纹理图案效果。但实际上,将CSS3的渐变和`background-size`配合的好的话,不仅仅可以制作简单的条纹效果,还可以制作一些复杂的纹理效果。虽然这些纹理在实际中不十分实用,但让你练手和熟悉CSS3的强大功能是足以。
条纹背景
最后更新于:2022-04-01 03:43:38
> 原文出处:http://www.w3cplus.com/css3/css-secrets/striped-backgrounds.html
## 问题
和其他视觉设计相关的媒体一样,在 Web 上各类大小、颜色、角度不同的纹理也非常流行。不过,实现这些纹理的技术却并不理想。通常,我们需要创建独立的位图,如果有需求变更的话都需要重新更改文件。有些开发者使用 SVG 替代位图,但是 SVG 仍然是一种独立的文件,而且其语法也不够友好。那么是否有一种出色的方法让我们直接在 CSS 中创建纹理呢?你会惊喜的发现,我们将在下面的介绍中逐步解决这一问题。
## 解决方案
首先,假设我们需要一个简单地垂直渐变,颜色从 `#fb3` 到 `#58a`:
![条纹背景](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc8446e23.png "条纹背景")
*图1注:我们初始化的渐变效果*
~~~
background: linear-gradient(#fb3, #58a);
~~~
现在,让我们将两种颜色的过渡点调的更近一些:
![条纹背景](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc84cf5a0.png "条纹背景")
*图2注:现在渐变占据了整体`60%`的高度,其余的部分都是纯色;颜色过渡点的位置在这里使用虚线标识出来*
~~~
background: linear-gradient(#fb3 20%, #58a 80%);
~~~
到此为止,容器顶部`20%`的部分是纯粹的 `#fb3`,底部`20%`的部分是纯粹的 `#58a`,所以实际上渐变的部分只占有了容器的`60%`。那么如果我们将颜色过渡点(color stops)调整地更近一些(比如 `40%` 和 `60%`,如下图所示),那么渐变的部分就会更小了。这就让我们很自然地联想到,如果颜色过渡点相同会发生什么呢?
![条纹背景](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc8f1b329.png "条纹背景")
*图3注:现在渐变占据了整体`20%`的高度,其余的部分都是纯色;颜色过渡点的位置在这里使用虚线标识出来*
~~~
background: linear-gradient(#fb3 50%, #58a 50%);
~~~
> "如果多个颜色过渡点的位置相同,那么就会在两个颜色之间生成一个无限小的过渡。实际效果就是,一个颜色不再会流畅地过渡到下一个颜色了,而是会突然变成下一个颜色。" — [CSS Image Values Level 3](http://w3.org/TR/css3-images)
正如下图中看到的那样,已经看不到颜色过渡区域了,只有两种纯色,每种纯色占有容器一半的高度。可以说,我们已经创建了两个宽大的水平纹理。
![条纹背景](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc8f44193.png "条纹背景")
*图4注:现在两个颜色的过渡点位置重合了*
因为渐变本质上就是 `backgroud-image`,所以我们可以像对待 `background-image` 一样使用 `background-size` 调整大小:
~~~
background: linear-gradient(#fb3 50%, #58a 50%);
background-size: 100% 30px;
~~~
就像下图看到的那样
![条纹背景](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc9484b45.png "条纹背景")
*图5注:我们生成的背景没有使用平铺*
我们将两条纹理都缩小到了 `15px` 的高度。因为背景是可以平铺的,所以我们可以让整个容器填充这种水平纹理了:
![条纹背景](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc94b7712.png "条纹背景")
*图6注:最终的水平渐变*
当然,我们还可以创建不等宽的条纹,秘诀就是调节一下颜色过渡点的位置:
~~~
background: linear-gradient(#fb3 30%, #58a 30%);
background-size: 100% 30px;
~~~
为了避免每次需求变更都需要修改两处颜色过渡点的重复工作,我们可以充分利用规范中介绍的原理:
> “如果某个颜色过渡点的位置小于它之前的颜色过渡点,那么该颜色过渡点的位置就会被重置为所有在它前面的颜色过渡点的最大位置。” — [CSS Images Level 3](http://w3.org/TR/css3-images)
这意味着如果我们将第二个颜色过渡点设置为`0`,那么它实际的位置就会被浏览器重置为它前面颜色过渡点的最大位置,而这个位置恰恰就是我们需要的过渡位置。因此,下面的代码不仅仅是和下图 具有同样的效果,而且更加简洁、更具有可维护性:
![条纹背景](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc94f0790.png "条纹背景")
*图7注:不等宽条纹*
~~~
background: linear-gradient(#fb3 30%, #58a 0);
background-size: 100% 30px;
~~~
创建更多颜色的纹理和创建两种颜色的问题同样简单。比如,下面的代码块创建了三种颜色的纹理:
![条纹背景](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc9f5bcfa.png "条纹背景")
*图8注:三色条纹*
~~~
background: linear-gradient(#fb3 33.3%,
#58a 0, #58a 66.6%, yellowgreen 0);
background-size: 100% 45px;
~~~
## 垂直纹理
水平纹理非常容器创建,但是在 Web 上并不是所有的纹理都是水平的,此外还有很多纹理是垂直的
![条纹背景](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdcb39c2a7.png "条纹背景")
*图9注:垂直条纹,上面:背景没有使用平铺;下面:平铺之后的条纹*
而且,在视觉上最受欢迎的纹理可能是倾斜的纹理。值得庆幸的是,CSS 的渐变可以帮助我们实现这样的效果,只是实现的难度各种不同。
实现垂直纹理的代码和水平纹理的非常相似,只有一个主要的差异:一个指定渐变方向的参数。我们可以通过指定这个参数来创建水平纹理,不过对于此次要创建的垂直纹理,使用它的默认值即可(`to bottom`)。此外,同样需要创建一个不同的`background-size`,原因很明显:
~~~
background: linear-gradient(to right, /* or 90deg */
#fb3 50%, #58a 0);
background-size: 30px 100%;
~~~
## 斜纹
在实现了水平纹理和垂直纹理之后,我们可能会尝试通过 `background-size` 和渐变方向来实现倾斜的纹理(`45°`),就像这样的代码:
~~~
background: linear-gradient(45deg,
#fb3 50%, #58a 0);
background-size: 30px 30px;
~~~
不过,就像下图所示,效果非常不好。
![条纹背景](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdcb3e8ecb.png "条纹背景")
*图10注:第一次创建斜纹的失败效果*
究其原因,就是因为我们只是将每一条纹理旋转了 `45°`,旋转的并不是图形整体。让我们回忆一下使用位图创建斜纹的方法,其中引入的位图和下图相类似。
![条纹背景](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdcb4274f3.png "条纹背景")
*图11注:这就是创建斜纹的拼接图,看起来是不是很像?*
它包含了四条纹理,而不是这里的两条,所以看起来像是无缝连接的。这就是我们需要在 CSS 中重新创建的纹理,所以我们需要更多的颜色过渡点:
~~~
background: linear-gradient(45deg,
#fb3 25%, #58a 0, #58a 50%,
#fb3 0, #fb3 75%, #58a 0);
background-size: 30px 30px;
~~~
完成后的效果:
![条纹背景](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdcb49446c.png "条纹背景")
*图12注:`45°` 斜纹;虚线用来标识复用的区块*
如你所见,虽然我们成功的创建了斜纹,但是看起来比水平和垂直纹理要窄一些。为了回答这个问题,我们需要使用学校里学到的勾股定理来计算直角三角形的各边变长。勾股定理指出,最长边等于其他两边的平方和。在直接三角形中,两条短边相等所以么最长边就等于:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdcb7142a2.png)
在我们创建的这个斜纹中,`background-size` 指定的就是三角形最长边的边长,因此纹理的宽度就是直角边的长度。你可以看下图,获得更清晰的解释。
![条纹背景](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdcb7534be.png "条纹背景")
图13注:大小为 `20px` 的 `background-size` 将会生成宽度为![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdcb77cd0d.png)的条纹
这就是说,如果想要获得原来 `15px` 的宽度,就需要将 `background-size` 指定为![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdcb7a6f1e.png):
~~~
background: linear-gradient(45deg,
#fb3 25%, #58a 0, #58a 50%,
#fb3 0, #fb3 75%, #58a 0);
background-size: 42.426406871px 42.426406871px;
~~~
最终效果:
![条纹背景](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdcb80b445.png "条纹背景")
*图14注:`45°`斜纹的最终效果;值得注意的是,现在条纹的宽度和其示例中条纹的宽度一致了*
不过,除非某人拿枪威胁你非得让斜纹宽度为 `15px`(这种情况你你死定了,因为根号二是一个有理数,所以这里虽然使用了一个高精度的数值,但仍然不是它的值),否则我建议你可以约取一个数值,比如 `42.4px` 或者 `42px`。
## 更出色的斜纹
上一节介绍的方法使用起来不够灵活。如果我们让斜纹倾斜 `60°`、`30°`、`3.1415926535°` 时又该怎么处理呢?如果我们只是想修改一下角度,效果看起来还是不错的(下图是一个失败的尝试)。
![条纹背景](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdcb85c95f.png "条纹背景")
*图15注:失败的 `60°` 斜纹*
所幸的是,有一种更好的方式创建斜纹。很少人知道的是,其实 `linear-gradient()` 和 `radial-gradient()`也有一个实现平铺效果的版本:`repeating-linear-gradient()` 和 `repeating-radial-gradient()`。它们的原理基本相同,只有一个差别:颜色过渡点也可以无限平铺,知道填充整个图片。所以,可以这样平铺渐变:
![条纹背景](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdcb889d7f.png "条纹背景")
*图16注:平铺后的线性渐变*
~~~
background: repeating-linear-gradient(45deg,
#fb3, #58a 30px);
~~~
这段代码与下面的线性渐变代码等效:
~~~
background: linear-gradient(45deg,
#fb3, #58a 30px,
#fb3 30px, #58a 60px,
#fb3 60px, #58a 90px,
#fb3 90px, #58a 120px,
#fb3 120px, #58a 150px, ...);
~~~
你可能已经猜到了,平铺线性渐变同样适用于斜纹。因为它们的平铺特性,这意味我们的整个背景都可以使用渐变图片来实现了。因此,我们无需再担心创建平铺小图片的无缝连接问题了。
为了做一下比较,下图中的背景也可以使用下面的平铺渐变来实现:
![条纹背景](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdcb8ca8d1.png "条纹背景")
*图17注:`60°` 条纹*
~~~
background: repeating-linear-gradient(45deg,
#fb3, #fb3 15px, #58a 0, #58a 30px);
~~~
这种方法最明显的优势就是减少了重复:如果需要修改颜色,那么只需修改两处即可完成,而之前则需要三次。另一个值得注意的地方在于,现在我们可以使用颜色过渡点来控制颜色的宽度,而不再使用 `background-size,background-size` 专注于控制元素的尺寸。这意味着定义条纹的宽度会更加直接,无需再计算 根号二这样的数了!
不过,最大的好处还在于现在可以任意调整倾斜的角度了,而且无需再考虑纹理块之间的无缝连接问题。比如,这里有一个 `60°` 的条纹示例:
![条纹背景](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdcb8ca8d1.png "条纹背景")
~~~
background: repeating-linear-gradient(60deg,
#fb3, #fb3 15px, #58a 0, #58a 30px);
~~~
简单到只需调节角度即可实现!值得注意的是,使用这种方法创建两条斜纹的话,我们需要设置四个颜色过渡点。这意味着,使用第一种方式创建水平和垂直纹理更方便,而使用这种方式更适合于创建斜纹。如果我们要创建 `45°`的斜纹,那么可以组合使用这两种方法,核心是使用 `repeating-linear-gradient` 来缩简重复代码:
~~~
background: repeating-linear-gradient(45deg,
#fb3 0, #fb3 25%, #58a 0, #58a 50%);
background-size: 42.426406871px 42.426406871px;
~~~
> **展望:拥有两个位置的颜色过渡点** 在不久的将来,我们将可以为同一个颜色过渡点设定两个位置,现在对此已经有了一个简单地规划,并添加进了 [CSS Image Values Level 4](http://w3.org/TR/css4-images)。在这里,可以将新语法看成一个简写形式,只需要添加两个颜色过渡点以及不同的过渡位置,就可以创建一个纹理。举个例子来说,我们可以将上图的斜纹代码修改为: `background: repeating-linear-gradient(60deg, #fb3 0 15px, #58a 0 30px);` 这样的做不仅让代码更加容易理解,而且也更具有可维护性:不再需要重复定义过渡点颜色,只需编写一次即可实现纹理。不幸的是,在本书编写的时候,还没有任何浏览器支持它。
## 灵活可扩展的精致斜纹
通常来说,我们的斜纹并不是由多种不同颜色组成的,往往只是一种颜色的变化就可以构造成精致的纹理。比如,看看下面这个纹理:
~~~
background: repeating-linear-gradient(30deg,
#79b, #79b 15px, #58a 0, #58a 30px);
~~~
具体效果如下:
![条纹背景](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdcbc02fd6.png "条纹背景")
*图18注:添加了细微亮色的条纹*
你可以看到,组成该纹理的是同一色彩衍生的两种颜色,其中一种比另一种的亮度更高而已。不过,我们无法从代码中分辨出两个颜色之间的关系。此外,如果我们想改变色彩,那么至少需要在四个地方进行编辑。
所幸的是,这里有一个更好的编写方式:不再为纹理指定具体的颜色,只使用深色定义 `background-color`,然后它上面添加一个半透明白色的纹理:
~~~
background: #58a;
background-image: repeating-linear-gradient(30deg,
hsla(0,0%,100%,.1),
hsla(0,0%,100%,.1) 15px,
transparent 0, transparent 30px);
~~~
最终的结果上图一模一样,但我们现在只需编辑一处就可以修改成不同的色彩。使用这个方法,我们甚至还得到了一个额外的好处,那就是为低版本浏览器提供了降级处理,当浏览器不支持渐变时,就会显示深色背景。在下一节中,我们将会看到,使用透明区域的渐变图案,可以让我们创建更复杂的图案。
## 扩展阅读
* [CSS3 Gradient](http://www.w3cplus.com/content/css3-gradient)
* [再说CSS3渐变——线性渐变](http://www.w3cplus.com/css3/new-css3-linear-gradient.html)
* [再说CSS3渐变——径向渐变](http://www.w3cplus.com/css3/new-css3-radial-gradient.html)
* [Web页面的纹理背景设计与资源](http://www.w3cplus.com/source/background-patterns-designs-and-resources-for-websites.html)
* [37个CSS3 Patterns Gallery](http://www.w3cplus.com/demo/413.html)
* [Stripes in CSS](https://css-tricks.com/stripes-css/)
* [CSS3 Patterns, Explained](https://24ways.org/2011/css3-patterns-explained/)
* [SVG Patterns Gallery](http://philbit.com/svgpatterns/)
* [CSS3 Patterns Gallery](http://lea.verou.me/css3patterns/)
* [How to Use SVG Patterns](http://designmodo.com/svg-patterns/)
## 总结
这一节从CSS3的线性渐变开始,详细介绍了如何通过线性渐变配合`background-size`实现双色、多色斑马纹理背景效果。而且将线性渐变的方向换成角度值,可以实现斜纹纹理背景。当然,事实上,不同的背景(渐变做的背景)配合`background-size`可以实现更多的纹理背景。感兴趣的同学,看完之后可以参阅文章中的扩展资源,了解更多相关知识。
内凹圆角
最后更新于:2022-04-01 03:43:35
> 原文出处:http://www.w3cplus.com/css3/css-secrets/inner-rounding.html
## 问题
有些时候,我们只希望容器的内部边框是圆角的,但是外部轮廓线要是矩形的,如下图所示:
![内凹圆角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc0d7e385.png "内凹圆角")
*图1注:一个具有轮廓线的容器,而它的圆角只在容器的内部*
目前,这个有趣的设计用得还不是很多。使用两个元素实现这种效果就太平庸了:
~~~
<div class="something-meaningful"><div>
I have a nice subtle inner rounding,
don’t I look pretty?
</div></div>
.something-meaningful {
background: #655;
padding: .8em;
}
.something-meaningful > div {
background: tan;
border-radius: .8em;
padding: 1em;
}
~~~
这么做的效果还不错,但会强迫我们使用两层标签,而实际上我们只需要一层。那么是否有方法只使用一层就实现这种效果呢?
## 解决方案
之前使用两层标签的解决方案很灵活,可以让我们充分利用 `background` 的特性。比如,我们希望边框不只是纯色的,也有可能是带有噪点的纹理,那么使用上面的方法很简单就可以做到。不过,当我们的边框只是纯色时,有一种方法可以只使用一层元素实现相同的效果,下面就是代码:
~~~
background: tan;
border-radius: .8em;
padding: 1em;
box-shadow: 0 0 0 .6em #655;
outline: .6em solid #655;
~~~
你能猜到这段代码的效果吗?它所表现的效果:
![内凹圆角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc0dbd545.png "内凹圆角")
*图2注:为一个圆角元素添加轮廓线*
在这里我们将会充分利用 `outline` 不受元素的圆角影响,而元素的阴影受圆角影响的特性来完成这一效果。因此,如果我们将`outline` 置于图层顶部,那么 `box-shadow` 就会弥补 `outline` 在角落上的空隙:
![内凹圆角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc0de8d12.png "内凹圆角")
*图3注:使用 `box-shadow` 属性包围圆角,并且不设置任何偏移和模糊效果*
所以组合使用它们就可以实现最终的效果。下图中的阴影和 `outline` 被设置成了不同的颜色,这是为了让大家看的更明白。
![内凹圆角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc0e2f4ad.png "内凹圆角")
*图4注:这里的轮廓线之所以使用黑色,阴影之所以使用洋红色,是为了让大家看清其中的原理;值得注意的是轮廓线在所有图层的最上方*
注意,我们无需让阴影的范围等于 `outline` 的宽度,只需让阴影足以覆盖间隙即可。实际上,如果让 `outline` 的宽度和阴影的范围相同,在某些浏览器中会发生一些渲染问题,所以我建议使用比 `outline` 稍小一些的阴影。这也提出了新的问题:弥补间隙的最小阴影应该是多少?
为了回答这个问题,我们需要使用学校里学到的勾股定理来计算直角三角形的各边变长。勾股定理指出,最长边等于其他两边的平方和,由此可知,如果这两条边都等于 `a`,那么最长边就等于![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc136b7d0.png)。
你可能会惊讶中学学到的理论竟然和我们的内部圆角效果相关,那么你可以查看一下图:
![内凹圆角](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc13ac456.png "内凹圆角")
*图5注:如果边框半径是`r`,那么圆心到罗廓线边角的最长距离就是![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc13e7476.png),这意味着阴影最小的扩散范围应该是![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc142b063.png)。*
它很好地解释了勾股定理是如何应用到这里的。在我们的这个示例中,`border-radius` 是 `.8em`,所以最小的阴影范围是![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc149ee8f.png)。在这里我们只需要将它约取为 `.34em` 的阴影范围。为了避免重复计算,你可以直接取圆角半径的一半,这样就已经足够用了![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc14bb5ba.png)。
请注意,这种计算方法并不是没有限制的:对于这里要实现的效果,我们阴影半径必须比 `outline` 的宽度要小,同时还要大于![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc14e3e84.png)(这里的 `r` 表示 `border-radius`)。这意味着如果 `outline` 的宽度小于![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdc14e3e84.png)那么我们就无法实现实现这种效果了。
灵活的背景定位
最后更新于:2022-04-01 03:43:33
> 原文出处:http://www.w3cplus.com/css3/css-secrets/flexible-background-positioning.html
## 问题
我们经常遇见的一个问题就是,需要将背景图片定位到不同的位置,往往不仅是左上角,比如右下角。在 CSS 2.1 中,只能指定图片相对左上角的偏移量,或者使用关键字定位到其他的角落。不过,通常希望在背景图片和边角之间保留一些空间,避免类下图的效果。
![灵活的背景定位](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdba331325.png "灵活的背景定位")
*因为图像和边缘之间没有间隔,所以 `background-position: bottom right;` 实现的效果通常并不美观*
对于具有固定尺寸的容器,在 CSS 2.1 中是可以解决这个问题的,但是使用的技术比较杂乱:我们需要计算出背景图片相对左上角的偏移量,模拟出背景图片和左下角之间的间隙。但是,对于尺寸不固定的元素(由于内容不固定导致的尺寸不固定),这种方法就无能为力了。开发者通常喜欢使用百分比设置背景位置,通常是略小于 `100%` 的值,比如 `95%`。当然,使用最新的 CSS,我们对这个问题有更优雅的解决方法。
## 解决方案:增强型 `background-position`
在 [CSS Backgrounds & Borders Level 3](http://w3.org/TR/css3-background) 中,增强型的 `background-position` 属性已经开始支持针对每个边角的偏移量了,开发者需要做的就是在偏移量之前加上边角的信息。比如,如果我们想要背景图片距离右侧边界 `20px`,距离底部边界 `10px`,那么就可以这么做:
~~~
background: url(code-pirate.svg) no-repeat #58a;
background-position: right 20px bottom 10px;
~~~
完成后的效果如下图所示:
![灵活的背景定位](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdba3ab793.png "灵活的背景定位")
*不同的边上指定偏移量,如图所示的虚线轮廓*
最后需要做的就是设置降级措施。目前,大多数的浏览器尚不支持新式的 `background-position` 语法,虽然编写了上述代码,在某些浏览器背景图片仍然会被定位到左上角,看起来非常难看,并且遮挡住部分文字,如下图所示效果:
![灵活的背景定位](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdba3e9079.png "灵活的背景定位")
*如果我们不希望低版本浏览器的用户看到这样的效果,就需要提供一种降级处理*
一个简单降级方式是在 `background` 缩写属性中添加定位属性 **bottom right** :
~~~
background: url(code-pirate.svg)
no-repeat bottom right #58a;
background-position: right 20px bottom 10px;
~~~
## 解决方案:`background-origin`
通常,我们只需要给容器添加内边距就可以模拟背景图片和边界之间的偏移。如果使用上一节讲述的 `background-positon` 来做这件事,那么代码就会像这样:
~~~
padding: 10px;
background: url(code-pirate.svg) no-repeat #58a;
background-position: right 10px bottom 10px;
~~~
最终效果如下图所示:
![灵活的背景定位](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdba95bdb6.png "灵活的背景定位")
*为背景图添加的偏移量正好等于内边距的值*
如你所见,效果还不错,但是这些代码并不够简洁:如果需求发生变化需要修改代码,那么我们需要修改三处代码才能完成。所幸还有一种简单的方式可以代替它,无需重复声明内边距即可让背景图片根据内边距调整偏移量。
在你的web开发生涯中,你可能会写许多次类似 `background-position: top left;` 的属性。那么你是否思考过:哪里是左上角(`top-left corner`)?或许你已经知道了,每一个元素都有四个盒模型(如下图所示):外边距盒模型(margin box)、边框盒模型(border box)、内边距盒模型(padding box)以及内容盒模型(content box)。那么 background-position 中所指的 top left 指的是哪一个盒模型呢?
![灵活的背景定位](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdba99c518.png "灵活的背景定位")
实际上,`background-position`默认上值得就是内边距盒模型,所以边框最终遮盖了背景图片。因此,`top left` 默认上指的就是内边距盒模型的左上角。不过在 [Backgrounds & Borders Level 3](http://www.w3.org/TR/css3-background/) 中,我们拥有了新的属性用来改变这种默认行为:`background-origin`。你可能已经猜测到了 ,它的默认值也是 `padding-box`。如果我们就像下面的代码一样=将其更改为 `content-box`,那么 `background-pisition` 就会将`content-box` 视为背景图片的存在范围(实际上,这意味着所有的背景图片将会根据内边距实现和边框的偏移):
~~~
padding: 10px;
background: url("code-pirate.svg") no-repeat #58a
bottom right; /* or 100% 100% */
background-origin: content-box;
~~~
现在视觉效果和前面示例相同了,不过更胜一筹的是现在的代码更简洁。一定要牢记,如有需要你可以将上述的两种方法组合起来使用。如果你想使用不同的内边距,同时又想让其向外突出或向内嵌入,那么就可以组合使用 `background-origin: content-box` 和 `background-position`。
## 解决方案:`calc()`
让我们重新审视一下我们遇到的挑战:我们需要将背景图片定位到离右边框 `20px`、离底部边框 `10px`的位置。不过,使用这样解决方案,我们需要将其视为从左上角的偏移量,所以可以视为在水平方向偏移 `100% - 20px`、在垂直方向偏移 `100% - 10px`。所幸的是,`calc()` 函数就可以帮我们完成这种计算,并且可以完美地融入 `background-position` 属性:
~~~
background: url("code-pirate.svg") no-repeat;
background-position: calc(100% - 20px) calc(100% - 10px);
~~~
> 在 `calc()` 中,不要忘记在加减号操作符左右添加空格,否则就会发生解析错误!这种奇怪的规则是为了兼容性:因为在未来,`calc()` 中可能会允许使用关键字,而这些关键字中就有可能会包含连字符。
## 总结
在 CSS 2.1 中,只能指定图片相对左上角的偏移量,或者使用关键字定位到其他的角落。如果需要让背景图距离元素四边距有一定的偏移量,相对而言是较为麻烦。在这篇文章中,作者通过`background-position`新特性、修改`background-origin`以及`calc()`函数等三种方法,实现了一种灵活型的背景图定位。希望这篇文章对大家有所帮助。
多边框
最后更新于:2022-04-01 03:43:31
> 原文出处:http://www.w3cplus.com/css3/css-secrets/multiple-borders.html
## 问题
[Backgrounds & Borders Level 3](http://www.w3.org/TR/css3-background/) 还是一份草案的时候,CSS WG 就在是否支持多重边框的问题进行了大量的探讨,就像是讨论多重背景图片一样。不幸的是,当时认为多重边框的用处并不大,即使需要也可以使用[`border-image`](http://www.w3cplus.com/content/css3-border-image)属性来模拟。不过,WG显然忘记了在 CSS 代码中灵活地调整边框,才是我们需要的,现在开发者只能使用一些Hack手段来模拟多重边框,比如使用多重元素的嵌套来模拟多重边框。现在,我将告诉你一些更优秀的方法,无需使用多余的标签即可实现多重边框。
## box-shadow解决方案
现在,大多数的时候都用[`box-shadow`](http://www.w3cplus.com/content/css3-box-shadow)来创建阴影。不过,很少有人知道它还接受[第四个参数(spread)](http://www.w3cplus.com/css3/css3-box-shadows-unnoticed-spread),该参数可以缩放阴影的范围。比如下面的示例,我们创建了一个水平和垂直偏移量为`0` 的阴影,它就使用了上述所说的第四个参数:
~~~
background: yellowgreen;
box-shadow: 0 0 0 10px #655;
~~~
效果如下图所示:
![多重边框](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdb45ac962.png "多重边框")
*图注:使用 `box-shadow` 模拟边框线*
这还不够令人惊讶,因为它和我们使用 `border` 创建的边框差不多。不过,强大的是我们可以使用逗号来创建任意数量的阴影。所以,只需要继续添加阴影就可以实现多重阴影了,比如,添加一个颜色为 **deeppink** 的边框:
~~~
background: yellowgreen;
box-shadow: 0 0 0 10px #655, 0 0 0 15px deeppink;
~~~
唯一需要牢记的事情就是,`box-shadow` 属性是叠在一起的,第一个阴影总是位于最顶层,所以你需要调整阴影的大小。比如,在上一段代码中,我们希望最外层的边框为 `5px`,那么我们就可以设置一个 `15px`(`10px + 5px`) 的阴影。如果你需要,那么就可以为指定任意层次的阴影:
~~~
background: yellowgreen;
box-shadow: 0 0 0 10px #655,
0 0 0 15px deeppink,
0 2px 5px 15px rgba(0,0,0,.6);
~~~
除了下述的少数情况外,使用 `box-shadow` 的解决方案都很好用:
* 阴影并不是边框,它们并不占有实际的空间,也不能归属于 `box-sizing` 的范围。不过,你可以通过使用内边距或外边距(取决于阴影是内部的还是外部的)占据额外的空间来模拟。
* 上述示例模拟的边框是位于元素外部的。它不能捕获类似悬停和点击的鼠标事件。如果事件很重要,那么可以通过添加 `inset` 关键字让阴影出现在元素的内部。注意,你可能需要添加额外的内边距来扩充空间。
## outline的解决方案
在某些情况下,如果我们只需要两层边框,那么我们只需要一层常规的边框和一层`outline` 就可以实现。这也让我们的边框在样式上保持了足够的灵活性(比如我们想要一个虚线边框),但是使用 `box-shadow` 的话,我们只能模拟出实线边框。如下图所示:
![多重边框](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdb45e55dc.png "多重边框")
*图注:使用 `box-shadow` 模拟两条轮廓线*
~~~
background: yellowgreen;
border: 10px solid #655;
outline: 15px solid deeppink;
~~~
使用 `outline` 的另一个好处就是,我们可以通过 `outline-offset` 控制 `outline` 到元素边框的距离,该属性甚至可以接受负值。这对很多效果都非常有用,如下图所示:
![多重边框](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdb4b4a85b.png "多重边框")
*注:对于虚线样式的轮廓线,通过给 `outline-offset` 设置负值,创建了一个基础的缝纫效果 *
不过,这种方法也有一些限制:
* 就像起初说的那样,这种方法只能模拟两层边框,因为每个元素只能创建一个 `outline`。如果需要创建多重边框,那么只能使用 `box-shadow` 的方法。
* `border-radius` 并不能让 `outline` 呈现圆角效果,所以,即使你的边框是圆角的,`outline` 仍然是直角的(如下图所示)。注意,CSS WG 认为这是一个 bug,在未来`border-radius`可能可以将 `outline` 变为圆角。
* 根据 [CSS User Interface Level 3 specification](http://www.w3.org/TR/css3-ui/) 的说法:“`outliens` 可能并不是矩形。”虽然大多数情况下它看起来像是矩形,但是在你使用这一方法时,还是需要进行跨浏览器测试的。
![多重边框](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdb4b71a90.png "多重边框")
*图注:通过`outline`创建的轮廓线并没有贴合元素的圆角,这个问题在未来可能会被修正*
## border-colors解决方案
[border-colors](http://www.w3cplus.com/content/css3-border-color)是一个独特的属性,从字面上来说就是为多边框而生,只可惜的是,到目前为止这仅是Gecko内核浏览器独有的属性。
如果不追究浏览器的兼容性,那么`border-colors`也可以实现多边框的效果。在具体使用时,需要分成四边边框来写:
~~~
-moz-border-top-colors: <color> <color> <color>*; /*顶边边框*/
-moz-border-right-colors:<color> <color> <color>*; /*右边边框*/
-moz-border-bottom-colors: <color> <color> <color>*; /*底边边框*/
-moz-border-left-colors: <color> <color> <color>*; /*左边边框*/
~~~
![多重边框](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cdb55a0f44.png "多重边框")
虽然这种方式也能实现多边框效果,但相比前两种解决方案而言要麻烦一些,来看一个简单示例:
~~~
border: 10px solid;
-moz-border-top-colors: red red red red green green green blue blue blue;
-moz-border-bottom-colors: red red red red green green green blue blue blue;
-moz-border-right-colors: red red red red green green green blue blue blue;
-moz-border-left-colors: red red red red green green green blue blue blue;
~~~
为了要实现三个颜色的多边框效果,`red`占`4px`,`green`占`3px`和`blue`点`4px`。需要大费周折。而且还不能直接写`border-colors`,因为浏览器不识别这样的属性。
除此之外,目前浏览器对这个属性支持度相当的弱。可以说,它只是Firefox的私有属性。
## 扩展阅读
* [Multiple Backgrounds and Borders with CSS 2.1](http://nicolasgallagher.com/multiple-backgrounds-and-borders-with-css2/)
* [Multiple Borders](https://css-tricks.com/snippets/css/multiple-borders/)
* [Multiple Borders with CSS](http://www.impressivewebs.com/multiple-borders-css/)
* [Multiple borders methods with CSS](https://carst.me/2011/08/multiple-borders-methods-css/)
## 总结
在CSS2.1的时代要实现[多边框的效果](http://nicolasgallagher.com/multiple-backgrounds-and-borders-with-css2/),大多数都是依赖于添加标签或依赖于伪元素实现。而今天实现多边框的方案也越来越多,比如`box-shadow`和`outline`等。虽然这些方法能更好的帮助大家实现多边框效果,但每一种方案都有其自己的利弊。不过随着技术的向前发展,说不定哪一天会有一个属性直接有支持多边框,就如Firefox的私有属性`-moz-border-<[top]|[right]|[bottom]|[left]>-colors`。让我们一起期待吧。
透明边框
最后更新于:2022-04-01 03:43:28
> 原文出处:http://www.w3cplus.com/css3/css-secrets/translucent-borders.html
## 问题
现在,你对 CSS 中的半透明颜色可能已经有了基础的了解,比如 **`rgba()`** 和 **`hsla()`**。从 2009 年开始,虽然在开发设计中使用它们需要付出一些代价,比如提供降级措施、使用IE滤镜,但是总得来说它是一个巨大的变革。不过在实际中,它们主要被用于背景,有这么几点原因:
* 早期的开发者没有意识到这些新属性就是类似 **#ff0066** 和 **orange** 的色彩,而是将它们看作是图片,所以仅用在了背景上。
* 为背景提供降级方案比其他属性更简单。比如,可以直接使用单像素的半透明图片替代半透明背景。对于其他属性,则只能使用不透明颜色。
* 在其他属性上使用半透明颜色,比如边框,并不简单,我们将在下文介绍原因。
![透明边框](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cda933fc7a.png "透明边框")
*图注:24ways.org 是第一个在设计中使用半透明颜色的网站,那时还是 2008 年,那时它们网站的特点就是使用了大量的背景(由 Tim Van Damme 设计)*
假设我们想要美化一个容器,让它拥有一个白色的和半透明白色的边框——这个半透明边框可以让它后面的内容显示出来。我们要做的第一步就像是下面这样:
~~~
border: 10px solid hsla(0,0%,100%,.5);
background: white;
~~~
除非你非常了解`background`和`border`这两个属性的工作原理,否则上面代码得到的结果会让你感到非常的困惑。边框哪里去了呢?是不是不能为边框添加半透明颜色呢?到底该怎么做?
![透明边框](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cda999ce06.png "透明边框")
*图注:第一次尝试实现半透明边框*
## 解决方案
虽然看起来的效果和预期有所差异,其实边框是存在的。实际上,背景色默认会扩展到边框上,这一点可以通过给边框添加虚线观察到。如下图所示:
![透明边框](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cda9a0ecee.png "透明边框")
*图注:背景默认会扩展到边框区域下方*
虽然这和你使用不透明的实线边框是一样的,但在这个示例中,它完全推翻了我们的预期。结果看起来是纯白色的边框,实际上是一个半透明白色的边框,然后在它下面是白色的背景色。
在 CSS 2.1 中,这就是 `background` 的工作原理。我们不得不接受和使用它。值得庆幸的是,在[Backgrounds & Borders Level 3](http://w3.org/TR/css3-background)中,我们已经可以使用`background-clip` 属性来调整这一效果。`background-clip`的默认值为`border-box`,也就是说,背景色会填充到容器边框以及边框以内的地方,所以我们只需要将其修改为`padding-box`(让背景色在容器的内边距以及内边距以内填充)就可以实现所需要的效果了:
~~~
border: 10px solid hsla(0,0%,100%,.5);
background: white;
background-clip: padding-box;
~~~
更完善的效果如下图所示:
![透明边框](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cda9a4e548.png "透明边框")
*图注:修正了 `background-clip` 的问题*
## 总结
虽然在`border-color`上能运用[`rgba()`、`hsla()`](http://www.w3cplus.com/css3/playing-with-css3-colors)设置边框为半透明或完全透明,如果元素设置了背景颜色或背景图片的时候,会直接影响边框的透明颜色效果。特别是,要看到边框底下的内容时。造成这个现象是由于背景图片会延伸到边框底部。要解决这一问题,可以通过CSS3的[`background-clip`](http://www.w3cplus.com/content/css3-background-clip)来修正。[@Chris Coyier](https://css-tricks.com/author/chriscoyier/)早在2010年的时候就在[CSS-Tricks](https://css-tricks.com/)详细介绍过[这方面的知识](https://css-tricks.com/transparent-borders-with-background-clip/)。
CSS 编码技巧
最后更新于:2022-04-01 03:43:26
> 原文出处:http://www.w3cplus.com/css3/css-secrets/css-coding-tips.html
## 缩简代码
在软件开发过程中,保持代码的简洁和可维护性是最大的挑战,对于 CSS 来说,同样如此。实际上,可维护代码的一个重要特性就是要**缩简需求变化时所需修改的代码量**。假设放大一个按钮需要对十处代码做出修改,那么你就有可能遗漏其中的一些细节,如果这些代码本来就不是你写的,那么就更有可能发生这种疏漏。即使需要修改的细节显而易见,即使你准确地找到了这些细节,那么你也在无形中浪费了大量的时间——这些时间本该有更大的创造力。
此外,这并不只是应对未来的需求变化。可扩展的 CSS 在完成首次编写后,只需要用少量代码创建适当的变量,那么进行重写和覆盖时所需的代码量就会很少。下面让我们来看一个例子。
请先看一下下面的 CSS,它被用来美化下图所示的按钮:
![按钮](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cd9cc1e96c.png "按钮")
~~~
padding: 6px 16px;
border: 1px solid #446d88;
background: #58a linear-gradient(#77a0bb, #58a);
border-radius: 4px;
box-shadow: 0 1px 5px gray;
color: white;
text-shadow: 0 -1px 1px #335166;
font-size: 20px;
line-height: 30px;
~~~
这段代码在可维护性上有几点问题,让我们来修改一下。首先看到的就是字体的单位。如果我们想要更改字体大小(比如为了创建更大、更显眼的按钮),那么就需要同时修改行间距,因为它们在这里使用的都是绝对值。此外,这里的行间距并不能有效地与字体大小关联起来,以至于我们需要手动计算各种字体大小下的行间距。**当属性值相互关联时,应该在代码中体现它们的关联性。**对于上述的代码,行间距是行高的 `150%`,所以,使用下面的代码将更具可维护性:
~~~
font-size: 20px;
line-height: 1.5;
~~~
既然我们都写成了这样,为什么还要给字体大小指定一个绝对数值?虽然绝对数值易于使用,但是每次需求变化时你就需要重新修改,比如现在我们想让父级字体更大一些,那么就需要在样式表中使用绝对数值修改每一条相关的样式规则,显然这是不可取的。更好的方式是使用百分比或者类似 **`em`** 的单位:
~~~
font-size: 125%; /* 假设父级字体为 16px */
line-height: 1.5;
~~~
现在,如果我修改了父级字体大小,那么按钮也会相应的变大。不过,按钮看起来和之前不大一样了,如下图所示:
![按钮](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cd9cdbd08d.png "按钮")
这是因为其他的特效还是和之前一样袖珍,仍然不具有伸缩性。只需要使用类似 **`em`** 的单位,我们就可以将其他特效变成可伸缩的,最终所有的属性值都关联到了字体大小上。至此,我们只需修改字体大小就能控制整个按钮的大小了。
~~~
padding: .3em .8em;
border: 1px solid #446d88;
background: #58a linear-gradient(#77a0bb, #58a);
border-radius: .2em;
box-shadow: 0 .05em .25em gray;
color: white;
text-shadow: 0 -.05em .05em #335166;
font-size: 125%;
line-height: 1.5;
~~~
现在,按钮看起来很像是原始版本的放大版:
![按钮](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cd9d390e05.png "按钮")
*在这里,我们想要字体大小与父级字体相关联,所以使用了 **`em`**。在某些情况下,你想要让字体和根节点的字体相关联(比如`<html>` 节点的字体),但是这时使用 **`em`** 就会让计算变得非常复杂。此时,最好使用 `rem` 单位。虽然“相关性”在 CSS 中很重要,但你关联前需要考虑下什么元素需要被关联在一起。 *
请注意,在某些属性上我们仍然使用了绝对数值。**对于按钮的哪些效果需要可伸缩,哪些不需要可伸缩这一问题,应该主观判断而不能客观要求。**比如此处的按钮,不论按钮如果缩放,我们始终要求它的边框粗细为 `1px`。
不过,通常我们所需要修改的不只是按钮的尺寸。对配色的修改也是一个很重要的方面。比如,如果我们想要创建一个红色的“取消”按钮,或者是绿色的 “OK” 按钮又该如何做呢?一般来说,我们至少需要重写四条样式(**`border-color`**,**`background`**,**`box-shadow`**,**`text-shadow`**)。不用多说你也会理解,重新计算主体颜色(**`#58a`**)的亮度、理清所需颜色的亮度,将是一份非常麻烦的事情。此外,如果我们将按钮添加到了非白色背景的页面中,又该如何修改呢?实际上,上述按钮的灰色阴影只在白色背景下才会效果明显。
一个简单的修改方式是,对主体颜色的亮度叠加半透明的黑白色:
~~~
padding: .3em .8em;
border: 1px solid rgba(0,0,0,.1);
background: #58a linear-gradient(hsla(0,0%,100%,.2),transparent);
border-radius: .2em;
box-shadow: 0 .05em .25em rgba(0,0,0,.5);
color: white;
text-shadow: 0 -.05em .05em rgba(0,0,0,.5);
font-size: 125%;
line-height: 1.5;
~~~
> 提示:使用 `HSLA` 而不是 `RGBA` 表示半透明白色的好处在于,由于无需重复,它的字符更少,编写的更快。
接下来,我们可以使用不同的颜色重写背景颜色了,如下图所示:
![按钮](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cd9e7e907a.png "按钮")
~~~
button.cancel {
background-color: #c00;
}
button.ok {
background-color: #6b0;
}
~~~
现在,整个按钮的样式更加灵活了。不过,这个示例并没有阐述怎样让代码保持简洁,更多技巧请阅读接下来的内容。
### 可维护性 VS 简洁
有些时候,**代码的可维护性和简洁是相互敌对的。**就像是前面的按钮示例,最终的代码量比最初的代码量还增加了一些。请思考一下下面的这段代码,它的作用是为某元素设置边框粗细,除左边框外都设置为 `10px`:
~~~
border-width: 10px 10px 10px 0;
~~~
虽然只是一条样式,但是当我们修改边框的粗细时,至少要修改三处地方。如果我们将该样式写成两句,那么修改起来就会更加方便,而且可以预见的是也更加冗余阅读:
~~~
border-width: 10px;
border-left-width: 0;
~~~
> 有些人可能会争论说 `em` 单位才是 CSS 的第一个变量,因为它引用了 **`font-size`** 的值。大多数的百分比也是同样的角色,虽然这并不令人感到兴奋。
### currentColor
**[CSS Color Level 3](http://www.w3.org/TR/css3-color/)** 为开发者提供了许多新的颜色关键字,比如 **lightgoldenrodyellow**——虽然目前来看这并没多大用处。不过,其中也有一些非常有用的颜色关键字,比如从 SVG 借鉴来的 **`currentColor`**。实际上,该属性并不是一个固定不变的颜色值,它会根据 **`color`** 的属性值生成相应的属性值——这让它成为了 CSS 中的第一个变量。虽然它的作用有限,但意义重大。
> 有关于`currentColor`相关的介绍,可以阅读《[使用CSS的currentColor变量扩展颜色级联](http://www.w3cplus.com/css3/extending-the-color-cascade-with-the-css-currentcolor-variable.html)》一文。
让我们来举个例子,假设要让所有的水平线(`<hr>`)和文本保持统一的颜色,那么就可以使用 **currentColor**:
~~~
hr {
height: .5em;
background: currentColor;
}
~~~
你可能已经注意到了,现有的很多属性就有相同的特性。比如,如果没有给边框添加颜色,那么边框的颜色会被渲染为文本的颜色。这是因为 **`currentColor`** 同样是许多 CSS 颜色属性的初始值:**`border-color`**、**`box-shadow`**、**`outline-color`**……
未来,当我们可以 CSS 的原生属性控制颜色时,那时我们能够使用更多的变量,而 **`currentColor`** 也将会更有用处。
### 继承
虽然很多开发者意识到了 **`inherit`** 关键字的重要性,但往往会忘记使用它。**`inherit`** 可以被应用到任何的 CSS 属性上,并且会根据父级元素的属性计算出恰当的值(如果是伪元素,那么就会根据当前元素属性来计算)。比如,让表单元素和页面其他元素具有相同的字体,无需一一指定,直接使用 **`inherit`** 即可:
~~~
input, select, button { font: inherit; }
~~~
同样的道理,让超链接和页面文本具有相同的颜色,也可以使用 **`inherit`**:
~~~
a { color: inherit; }
~~~
**`inherit`** 关键字也同样适用于 **`background`** 之类的属性。比如,创建一个拼写气泡(speech bubbles),让它自动继承输入框的背景和边框,如下图所示:
![气泡](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cd9fc45897.png "气泡")
*一个拼写检查气泡,它获得了父级元素的背景色和边框。*
~~~
.callout {
position: relative;
}
.callout::before {
content: "";
position: absolute;
top: -.4em;
left: 1em;
padding: .35em;
background: inherit;
border: inherit;
border-right: 0;
border-bottom: 0;
transform: rotate(45deg);
}
~~~
## 相信你的眼睛,而不是数字
人类的双眼并不是完美的输入设备(input device)。有时候,精确的尺寸却在视觉上呈现了一种不精确的感觉。比如在视觉设计中,众所周知的是当元素垂直居中时,有些细节是无法察觉的。相反,元素需要略高于几何中心,才能被视为垂直居中。如下图所示:
![气泡](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cd9fcb3d62.png "气泡")
*在第一个矩形中,棕色的正方形经数学计算是垂直居中的,但视觉上并不是;在第二个矩形中,棕色的正方形被放置在了略高于中心的位置,但视觉上更像是垂直居中。*
同样在经典的设计中,众所周知的是类似 “O” 的圆形字符需要略大于矩形字符,只是因为我们的视觉会认为圆形略小与矩形。如下图所表现的效果。
![气泡](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cda10eb83d.png "气泡")
*圆形虽然看起来更小,但它的边界实际上和正方形是一致的。*
**类似的视觉错觉在视觉设计中非常常见**,往往需要进行适当的修正。一个非常常见的例子就是包含文字的容器内边距。这一问题的原因是容器忽略了文本的数量,可能是一个单词,也可能是好几段文字。如果我们为容器的四个边指定了相同的内边距,那么就会像下图所示:
![气泡](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cda1129beb.png "气泡")
*为容器的四个内边距添加相同的数值(`.5em`),但是容器内的文字让上下两边的内边距显得比左右两边更大一些。*
容器看起来并不协调。究其原因就是**在水平方向上字体形状更加连贯,导致我们的眼睛认为垂直方向上的多余空间都是内边距**。因此,我们在垂直方向上减少内边距,才能让四边的内边距看起来一致。如下图所示:
![气泡](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cda115d4d9.png "气泡")
*让左右两边的内边距更大一些(`.3em .7em`),整体看起来更协调了。*
## 响应式设计
[RWD (Responsive Web Design,响应式设计)](http://www.w3cplus.com/responsive)在过去几年很流行。然而,大家的焦点往往放在了响应式网站的重要性上,而忽略了什么才是响应式网站的优势。
开发响应式网站,常见的方式就是在多种分辨率上测试一个网站,然后通过不断地叠加媒体查询修正出现的问题。然而,每一次使用媒体查询都增加了未来维护 CSS 的负担,它们实际上并不值得被添加。未来维护这段 CSS 代码的开发者需要反复检查是否所有的媒体查询都生效了,而且还可能会去修改这些媒体查询,而这么繁多的细节往往又容易被遗忘,继而导致了某些断层。你添加的媒体查询语句越多,那么 CSS 的碎片化现象就会越严重。
这并不是说使用媒体查询是一个糟糕的方式。**使用方法得当,才会事半功倍。**不过,媒体查询应该是我们的终极解决方案,只有当其他方法都无法完成响应式设计,或者需要在或大或小的视区上完全更改页面布局时(比如将侧边栏转换为水平方向),才应该使用媒体查询。之所以这么说,是因为媒体查询本质上不是用来修复连续性错误的。媒体查询主用于指定视区的阈值(或者称之为断点),除非其他的代码也是灵活可扩展的,否则媒体查询只能在特定的分辨率起作用,本质上并没有解决问题。
不必多说相信你也会理解,**媒体查询的断点不应该由具体的设备来决定,而应该由设计本身来决定。**一方面是因为不同尺寸的设备太多(特别是我们需要为未来的设备而考虑),网站需要尽可能地适应各种分辨率;另一方面是因为在电脑桌面上可能以任意尺寸的窗口打开网页。如果你自信地认为自己的设计可以应用到各种窗口大小,那又何必担心某些特定设备的分辨率呢?
根据第九页“缩简代码”一节的原则来编写媒体查询,将会让你免于不断地重写断点内的样式,减少断点内的问题。
这里有一些技巧,可以让你避免添加无用的媒体查询:
* 使用百分比而不是固定宽度。如果不能使用百分比,那么就是用与视窗(Viewport)相关的单位(`vw`, `vh`, `vmin`, `vmax`),它们能够根据视区的宽度或高度生成相应的数值。
* 如果你想为分辨率更高的页面使用固定宽度,那么请使用 **`max-width`**,而不要使用 **`width`**,这样做的好处是即使没有使用媒体查询,仍然能够确保页面适配较低的分辨率。
* 不要忘记将可替换元素设为 **`max-width: 100%;`**,常见的可替换元素包括:**`img`**、**`object`**、**`video`** 和 **`iframe`**。
* 当需要将背景图填充整个容器时,使用 **`background-size: cover;`** 可以让图片在容器改变大小时仍然保持填充,增加代码的可维护性。不过,一定要牢记带宽不是无限的,在移动端页面加载大尺寸的图片,再使用 CSS 进行缩放是非常不明智的做法。
* 在栅格布局中添加图片或者其他元素时,最好浏览器根据视区的宽度来决定列数。使用弹性盒布局([Flexible Box,又被成为 Flexbox](http://www.w3cplus.com/blog/tags/157.html))、声明为**`display: inline-block`** 或者让文本环绕图片,都可以达到这一目的。
* 当使用多栏布局时,应该使用 **`column-width`** 而不是 **`column-count`**,这样的好处即使分辨率很低,内容也不会拥挤在一起,而会正常排布在一列之中。
通常来说,我们需要坚守的原则就是:**在媒体查询的断点中,使用流式布局和相对大小**。当设计足够灵活时,创建响应式布局并不会使用过多的媒体查询语句。在 2010 年末,Basecamp 的设计师关于这一话题写道:
> “可以证明的是,只需要在最终的产品上增加少许的 CSS 媒体查询语句,就可以让布局适应各种设备。如此简单的关键在于,布局本身就是流式的。所以对于视区较窄的小尺寸屏幕,我们所要做的就是压缩外边距,增加可利用空间,以及更改侧边栏布局。” —— [Experimenting with responsive design in Iterations](https://signalvnoise.com/posts/2661-experimenting-with-responsive-design-in-iterations)
如果你发现自己需要大量的媒体查询语句适应不同尺寸的屏幕,那么你需要重新审视一下自己的代码,因为十有八九,问题的根据不在于响应式的设计。
> 建议在媒体查询中使用 `em` 而不是 `px`。使用 `em` 可以在布局发生改变时自动进行缩放。
## 明智地使用简写形式
对于下面的两行代码,你可能会知道它们之间的差异:
~~~
background: rebeccapurple;
background-color: rebeccapurple;
~~~
前者是一个简写写法,它会创建一个颜色为 **`rebeccapurple`** 的背景,而后者(**`background-color`**)可能会得到一个粉色的渐变、一只猫的背景图……这是因为在后者之外,可能还存在一个 **`backgound-image`** 属性在发挥作用。这就是使用普通写法(longhands,比如这里使用的 **`background-size`**)时常会发生的问题:无法重设其他的属性,继而影响了最终的效果。
当然,你可以重设其他所有的属性(这里就不演示了),但你很有可能会有所遗漏。或者,CSS WG 在未来会引入更多的普通写法属性,那么你的代码又会失效。除非刻意地为某些元素使用普通写法属性,比如我们在第九页“缩简代码”一节中对按钮颜色所做的处理,否则不要畏惧缩写形式,它们是优秀的防御性代码,可以有效在未来保持可用性。
组合使用普通写法和简写写法也很有用。当某些属性的属性值是由逗号分隔的参数列表,那么使用组合方式就可以让减少代码中的重复,比如 **`background`** 属性。下面的示例就很好地解释了这一做法:
~~~
background: url(tr.png) no-repeat top right / 2em 2em,
url(br.png) no-repeat bottom right / 2em 2em,
url(bl.png) no-repeat bottom left / 2em 2em;
~~~
请注意,其中 **`background-size`** 和 **`background-repeat`** 的属性值对于每张图片都是相同的,而且还重复了三次。我们可以将重复的属性值移动到全写属性中,然后该属性根据 CSS 参数列表的扩展规则,扩展到所有相关列表的项目中:
~~~
background: url(tr.png) top right,
url(br.png) bottom right,
url(bl.png) bottom left;
background-size: 2em 2em;
background-repeat: no-repeat;
~~~
现在,我们只需要修改一次,就可以改变 **`background-size`** 和 **`background-repeat`** 属性了。
## 我应该使用预处理器吗?
你也许已经听过类似 [LESS](http://lesscss.org/)、[Sass](http://sass-lang.com/) 和 [Stylus](http://learnboost.github.io/stylus) 等预处理器的大名了。它们为编写 CSS 提供了许多方便的特性,比如变量、混合宏、函数、嵌套规则、颜色操纵方法等等。
**合理使用预处理器,可以在大型项目中保持代码的简洁和灵活性**,而原生的 CSS 由于功能的缺乏往往限制了我们的开发。当我们坚持代码的健壮性、灵活性以及简洁时,我们就会时常受制于语言的能力。此外,预处理器本身也有一些缺陷:
* 无法追踪 CSS 文件的大小和复杂性。简洁短小的代码经过预处理器可能就会编译出冗杂的 CSS 代码来。
> 冷知识:怪异的简写语法 你可能已经注意到了,在简写写法和普通写法的示例中,为 **`background`** 简写写法指定**`bacground-size`** 时,需要额外添加 **`background-position`** 属性(虽然属性值就是默认值),并且需要使用反斜线分隔它们。为什么此类简写写法的语法如此怪异呢? 这么做主要是为了消除歧义。虽然在这个示例中 **`top right`** 很明显是 **`background-position`** 的属性、**`2em 2em`** 是 **`background-size`** 的属性,而且这些属性也是和顺序无关的。但是,对于 **`50% 50%`**,你觉得它是 **`background-size`** 的属性,还是 **`background-position`** 的属性呢?当你使用普通写法时,CSS 解析器能够了解你的意思,但是使用普通写法时,解析器就无法从 **`50% 50%`** 这一属性推断出它所指向的属性了。这就是在这里使用反斜线的目的了。
对于大多数的简写写法,很少有这种歧义,而且属性的顺序也没有限制。不过,让属性值的顺序对应合理的语法是一种非常好的做法,可以有效避免混淆和错误。如果你熟悉正则表达式和语法,那么也可以根据相关规范查看语法属性——这可能是判断属性值是否有特定顺序的最快方法。
* 因为在开发者工具中看到的 CSS 并不是你写的 CSS(由预处理器编译生成的),调试错误变得更加困难。鉴于可以通过 SourceMaps 获得调试支持,这已经不是什么难题了。SourceMap 是一个很棒的新技术,它可以告知浏览器生成的 CSS 在预处理器中的 CSS 中的行号,以此来缓解这个问题。
* 在开发流程中,使用预处理器具有一定的延迟。虽然它们的编译速度很快,但仍然会占用一定的时间编译 CSS,而在编译结束前你只能等待。
* 参与到我们代码库中的人,需要付出更多的努力以理解预处理器中的概念。对于我们的合作者,要么他们是熟悉预处理器的人,要么我们就必须教他们使用。所以我们对合作伙伴的选择受到了限制,不然我们就得花费额外的时间训练他们,二者两者都不是最优的方案。
* 不要忘记抽象泄露法则的教诲:“所有有意义的抽象,在一定程度上都是有漏洞的。”预处理器是人类开发的,就像人类开发的其他非凡语言,它们都是有漏洞的——这些漏洞往往难以察觉,而我们往往不会怀疑预处理器出错了,而会怀疑是 CSS 写错了。
除了上述列出的问题,预处理器也让开发者深深地依赖上了它们,即使在某些无需使用预处理器的小项目中,开发者也会惯性使然地使用它们,而开发者所不知道的,预处理器的大多特性都会在未来添加到原生的 CSS 中。惊喜吗?是的,**许多预处理器引以为傲的特性都会被融入原生的 CSS 中:**
* 已有一个类似变量的自定义属性草案被提出([CSS Custom Properties for Cascading Variables](http://www.w3.org/TR/css-variables-1/) )
* 源自 CSS Values & Units Level 3 的函数 **`calc()`** 不仅功能强大,而且也广受支持。
* [CSS Color Level 4](http://dev.w3.org/csswg/css-color) 中的 **color()** 方法就会提供操纵颜色的功能。
* 在 CSS WG 中已经就规则嵌套开展了多场严肃的讨论,并形成了一份草案。
请注意,上述提到的原生特性通常比预处理器提供的特性更加强大,因为它们是动态的。比如,预处理器是无法计算类似 **`100% - 50px`** 的表达式,因为在页面渲染完成前,预处理器是无法知道这里的百分比对应的具体数值是多少。与之相比,原生 CSS 的**`calc()`** 方法则可以轻松胜任这项工作。相同的是,预处理器的变量也无法像下面一样使用:
~~~
ul { --accent-color: purple; }
ol { --accent-color: rebeccapurple; }
li { background: var(--accent-color); }
~~~
你能理解这里做了什么吗?在有序列表中,列表项的背景色将会使用 **`rebeccapurple`**,而在无序列表中,列表项的背景色将会使用 **`purple`**。试试看预处理能不能做到!在这个示例中,虽然我们只使用了后代选择器,但重点在于展示原生 CSS 变量的动态性。
[![气泡](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cda11a13c3.png "气泡")](http://myth.io/)
*[Myth](http://myth.io/)是一个处于实验阶段的预处理器,它专注于模拟原生 CSS 的特性,而不是引入新的语法,非常类似于一个 CSS 的腻子脚本。*
因为上述的原生 CSS 特性尚未获得良好的支持,所以在大多数情况下使用预处理器是不可避免的。我的建议是在项目中以纯 CSS 起步,当不能保持代码的简洁时,切换为预处理器。为了避免完全依赖预处理器或在不适合的项目中使用预处理器,所以你要慎重的使用它们,而不是盲目的在新项目之初就使用它们。
如果你们想知道的话,那么我要告诉你接下章节中示例的样式就是使用 SCSS 编写的,虽然最初使用的是纯 CSS,但当代码变得复杂之后,为了可维护性切换为了 SCSS。谁说 CSS 和预处理器只能用于 Web?
Web 标准是友是敌?
最后更新于:2022-04-01 03:43:24
> 原文出处:http://www.w3cplus.com/css3/css-secrets/web-standards-friend-or-foe.html
## 标准化流程
与普通大众的理解所不同的是,W3C(World Wide Web Consortium,万维网联盟)实际上并不制定标准。对于 W3C 旗下的各个工作组(Working Groups, WG)来说,W3C 更像是一个论坛,聚集各种兴趣团体并让他们为某个标准而努力。当然,W3C 并不只是作为整个论坛的观察者:它制定整个论坛的基本规则并观察标准制定的整个流程。在撰写本书的时候,CSS WG 已经拥有了 98 个成员,其详细组成如下:
* **86** 名成员来自 W3C 的会员公司(88%)
* **7** 名受邀专家(7%)
* **5** 名 W3C 的全职员工(5%)
你可能已经注意到了,WG 的主要成员(88%)都来自 W3C 的会员公司。这些会员公司,包括了浏览器厂商、流行站点的维护商、研究机构和其他相关的常用技术公司,他们会因为 Web 标准化的流行而获益匪浅。W3C 的会员年费支撑了 W3C 基金会的主要开销,所以联盟可以免费、开源的分发它的各类标准和规范——这与一些需要缴纳授权费的标准化机构不同。
*受邀专家*是被邀请加入标准化制定流程的 Web 开发者,致力于解决规范制定初期调试时出现的连续性故障——他们往往具有非常深厚的技术背景。
最后,但非次要的是,W3C 还拥有一批为联盟工作的全职员工,他们的主要工作就是协调、促进 WG 和 W3C 之间的联络。
在 Web 开发者中一直有个普遍性的错误观念,认为 W3C 在上层制定了开发标准,然后各种浏览器无论是否愿意接受相关的标准,都必须去遵循和实践。实际上,这种观念跟现实相去甚远:对于接受哪些方面的标准,浏览器厂商比 W3C 更有话语权 —— 上面列出的数字就说明了这个问题。
另一个与普通大众的理解所不同的是:**标准不是凭空捏造的,制定标准也不是闭门造车**。CSS WG 所有的提交都是透明的,所有的交流都是对公众开放的,欢迎审查和参与到相关项目钟来:
* 大部分的交流都记录在它的邮件列表 [www-style](http://lists.w3.org/Archives/Public/www-style/) 中。www-style 是公开存档的,任何人都可以参与到其中。
* 然后,还有一个时长为一小时的 **每周电话会议(weekly telcon)**。它并不对 WG 成员之外的公众开放,但相关内容会被记录在 **[the W3C's IRC server](http://irc.w3.org/)** 的 **#css** 板块中。几天后,这些记录会被清除,然后发布到邮件刘表中。
* 最后,还有一个**季度性的面对面会议(quarterly face-to-face meetings)**,会议内容也会像电话会议一样被记录。如果获得 WG 主席们的同意,那么也可以旁听该会议。
上述所有的内容都只是 W3C 流程中与决策相关的一部分。不过,具体负责编写标准或规范的人,是规范编辑(*Spec Editos*)。规范编辑可能是来自 W3C 的全职员工、浏览器开发者、受邀专家,也可能是来自 W3C 会员公司的全职员工——这些员工由相关公司支付薪水,用以加快推动标准的制定和实施,从而获得公共利益。
每一份规范从诞生到成熟都要经历多个阶段:
* **编辑草案(Editor's Draft, ED):**在规范的第一个阶段,它的内容会比较混乱,只是规范编辑(Spac Editor)整理收集来的各种想法。对于处于这一阶段的规范,不附加任何必要条件,也不保证会被 WG 批准。此外,这也是修改版的第一个阶段:所有的修改内容首先要经过 ED,然后才能被发布。
* **第一次公开工作草案(First Public Working Draft, FPWD):**在这一阶段将会发布规范的第一个公开版本,此时往往认为规范可以接受来自 WG 的公开反馈了。
* **工作草案(Working Draft, WD):**在第一次公开工作草案发布之后还会有数个工作草案(WDs),新的工作草案会整合来自 WG 和其他更宽泛社区的反馈,进行增量优化。在这一阶段往往会出现第一个实现,但该实现并不是没有经历实验测试的低级版本。
* **候选推荐标准(Candidate Recommendation, CR):**该阶段的规范会被看做是相对稳定的版本,接着就会对它进行实现和测试。任何一个规范都不能在没有完整测试的情况下通过该阶段,而且至少要有两个独立的实现。
* **建议推荐标准(Proposed Recommendation, PR):**该阶段是 W3C 会员公司对规范发表异议的最后时机。这种异议很少发生,所以推动该阶段的规范进入最终阶段就只是时间的问题了。
* **推荐标准(Recommendation, REC):** W3C 规范的最后一个阶段。
WG 成员中的一到两位会成为 WG 主席。主席负责组织会议、协调电话、把握进度,基本上是总揽全局。担任主席一角是非常消耗时间和精力的事情,他们常常被形容为**在放牧一群猫**。当然,参与制定标准的每一个人都知道这种比较是没有实际意义的:毕竟放牧一群猫事实上要容易的多。
> 想要了解更多信息?Elika Etemad 编写了[一系列精彩的文章,详细介绍了 CSS WG 的运作方式](http://fantasai.inkedblade.net/weblog/2011/inside-csswg/)。强烈建议阅读一下。
## CSS3,CSS4 和其他的神奇特征
由 Håkon Wium Lie 和 Bert Bos 发布于 1996 年的 CSS 1 是一份非常简略的规范。它内容短小,以至于可以直接放在一个简单的 HTML 页面中,只需大约 64 张 A4 纸就可以打印出所有的内容。
发布于 1998 年的 CSS 2,其内容定义地更加严格,也包含了更多强大的特性,而且还吸纳了两个重要的规范编辑: Chris Lilley 和 Ian Jacobs。此时,它的内容量已经增长到了需要 480 页打印纸才能完全打印出来,几乎不可能被人类完全记忆。
在 CSS 2 之后,CSS WG 认识到了 CSS 的内容量太大,以至于不能将其归纳到一个简单的规范中。这不只让规范变得难以阅读和编辑,也阻碍了 CSS 的进一步发展。任何一个规范想要成为推荐标准,那么它的每一个标准至少需要拥有两个独立的实现并通过严格的测试。显然,这是不切实际的。因此,为了推动 CSS 的发展,CSS 被分隔成了多个模块,每个模块都有自己独立的版本。那些计划出现在 CSS2.1 中的特性因此拥有了一个 Level 3 的编号,比如下面的这些模块:
* [CSS Syntax](http://www.w3.org/TR/css-syntax-3/)
* [CSS Cascading and Inheritance](http://w3.org/TR/css-cascade-3)
* [CSS Color](http://www.w3.org/TR/css3-color/)
* [Selectors](http://www.w3.org/TR/selectors/)
* [CSS Backgrounds & Borders](http://www.w3.org/TR/css3-background/)
* [CSS Values and Units](http://www.w3.org/TR/css-values-3/)
* [CSS Text](http://www.w3.org/TR/css-text-3/)
* [CSS Text Decoration](http://www.w3.org/TR/css-text-decor-3/)
* [CSS Fonts](http://www.w3.org/TR/css3-fonts/)
* [CSS Basic User Interface](http://www.w3.org/TR/css3-ui/)
不过,模块化这一新概念是从 Level 1 开始的,比如下面的这些示例:
* [CSS Transforms](http://www.w3.org/TR/css-transforms-1/)
* [Compositing and Blending](http://www.w3.org/TR/compositing-1/)
* [Filter Effects](http://www.w3.org/TR/filter-effects-1/)
* [CSS Masking](http://www.w3.org/TR/css-masking-1/)
* [CSS Flexible Box Layout](http://www.w3.org/TR/css-flexbox-1/)
* [CSS Grid Layout](http://www.w3.org/TR/css-grid-1/)
**虽然 “CSS3” 这个词很流行,但实际上并没有类似 CSS 2.1 以及之前版本那样完整的规范。**相反,大多数作者只是粗略地引用了一些 Level 3 和 Level 1 的规范。虽然对于 “CSS3” 在作者中有一个共识性的规范范围,但因为过去几年 CSS 模块发展地非常不均衡,所有越来越难用类似 CSS3、CSS4 等术语来定义一个一致性的术语了。
## 浏览器前缀的冰火两重天
在标准的开发过程中,一直有一个“第二十二条军规”(源自美国作家约瑟夫·海勒的著作《第22条军规》,引申为荒唐的事情):标准制定小组需要根据开发者的需求创建规范。然而,开发者通常没兴趣去测试那些不会应用到产品中的特性。当实验性特性被广泛用于线上产品中时,WG 被迫跟进相关的技术,避免对现有网站的破坏。显而易见,开发者不提前测试相关标准,导致了后期开发受制于历史因素。
过去几年,业界提出了许多种方案来解决这个问题,但都不够完美。普遍受人厌恶的浏览器前缀就是其中一种方案。该方案的具体做法是,对于浏览器中的实验性特性(甚至是专有特性),提供一种附加浏览器前缀名的命名方案。最常见的前缀就是 Firefox 的 `-moz`,IE 的 `-ms-`,Opera 的 `-o-` 以及 Safari 和 Chrome 的 `-webkit-`。开发者可以毫无限制地使用前缀命名方式调用测试新特性,然后将使用信息反馈给 WG——WG 获得反馈之后可以进一步测试,直至完善。因此,即使在最终的标准化规范中相关特性拥有了不同的名字(没有前缀),也不会与现有的属性(有前缀)相冲突。
听起来还不错,是吧?如你所知,现实与理想总是有差距的。使用具有浏览器前缀的实验性特性让开发者认识到,创建页面特效原来可以这么简单,以至于他们开始在任何地方使用这些特性。使用具有浏览器前缀的属性成为了 CSS 的一种流行趋势:CSS 的教程里用这些属性、StackOverflow 的回答里用这些属性……很快,每一个独立的 CSS 开发者也开始毫无顾忌地使用它们。
最终,开发者们认识到了,只添加既有的浏览器前缀将会让他们不停地维护过去的项目:当其他浏览器实现了他们偏爱的酷炫特性时,他们必须手动地再去添加一次。不用多说相信你也会理解,不停地跟进这些新特性是一件非常痛苦的事情。那有没有什么解决方案呢?提前添加好所有可能的浏览器前缀,包括那些尚未实现该属性的浏览器前缀,从而达到防患于未然的目的。最终,我们可能就会像下面这样编码:
~~~
-moz-border-radius: 10px;
-ms-border-radius: 10px;
-o-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
~~~
上面的代码中有两处完全是多余的:`-ms-border-radius` 和 `-o-border-radius` 永远不会生效,因为 IE 和 Opera 从一开始就实现了无前缀版本的 `border-radius` 属性。
显然,将每种属性重复声明五次是单调乏味和不可维护的。看来使用工具实现自动添加只是一个时间问题了:
* 类似 **[CSS3 Please!](http://css3please.com/)** 和 **[pleeease](http://pleeease.io/playground.html)** 的网站,可以在你粘帖未加浏览器前缀的属性后,返回给你一个添加了所有可用浏览器前缀的属性。此类应用是最早一批尝试自动补全浏览器前缀的先驱,但是随着其他方案的崛起,该方案显得越来越笨拙,已经不再那么流行了。
* [Autoprefixer](https://github.com/postcss/autoprefixer) 通过使用 [Can I Use...](http://caniuse.com/) 的数据库,更智能地判断属性应该添加哪些浏览器前缀,并且能像预处理器一样在本地进行编译和执行。
* 我个人开发的 [-prefix-free](http://leaverou.github.io/prefixfree/) 可以直接在浏览器本地进行特征检测,从而判断所需的浏览器前缀。这样做的好处是很少需要更新该工具,它从浏览器本地环境获取所需的数据,包括必须的属性列表。
* 类似 [LESS](http://lesscss.org/) 和 [Sass](http://sass-lang.com/) 的预处理器虽然没有提供任何直接的浏览器前缀工具,但是有很多开发者为常用的属性创建了混合宏,目前也有几个流行的混合宏库。
由于开发者使用无前缀属性保证代码在未来的可用性,所以基本不可能改变这种编程习惯。基本上,我们只能使用早期不完善的规范来做开发,实际使用过程中可以改变的地方极其有限。不用多长时间,每个人都会认识到浏览器前缀就是个悲剧。
近些日子,新的实验性属性已经很少使用浏览器前缀了。相反的是,要想使用实验性属性就需要在浏览器设置中开启相关选项,因为开发者不能告诉用户开启相关选项来浏览页面,所以这种方法可以有效防止开发者将这些属性应用到线上环境中。当然,这样做的后果就是很少有开发者会把玩这些实验性属性。但是,我们还是能够获得足够的反馈——可以肯定的说,相比较使用浏览器前缀的方案,这种反馈的质量更高。不过,浏览器前缀的困扰还会存在很长时间。
前言
最后更新于:2022-04-01 03:43:21
《[CSS Secrets](http://shop.oreilly.com/product/0636920031123.do)》是[@Lea Verou](http://leaverou.com/)最新著作,这本书讲解了有关于CSS中一些小秘密。是CSSer值得一读的一本书,经过一段时间的阅读,我、[@南北](http://weibo.com/sunchongsheng)和[@彦子](http://weibo.com/793617505sy)一起将在W3cplus发布一系列相关的读后感,与大家一起分享。
[![CSS Secrets](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-01_560cd93ad3123.jpg "CSS Secrets")](http://shop.oreilly.com/product/0636920031123.do)