前言:
本系列是阅读《CSS揭秘》所作的总结,用以提高知识吸收程度。同时该书本身便是以解决 47 个 CSS 技巧而进行组织的,总结归纳后方便日后查找。

1. 半透明边框

难题

在 CSS 中使用半透明颜色,可以使用 rgba() 和 hsla()。

HSL代表色相,饱和度和亮度 - 使用色彩圆柱坐标表示。
HSL颜色值指定:HSL(色调,饱和度,明度)。
色相是在色轮上的程度(从0到360)-0(或360)是红色的,120是绿色的,240是蓝色的。饱和度是一个百分比值;0%意味着灰色和100%的阴影,是全彩。亮度也是一个百分点;0%是黑色的,100%是白色的。

HSLA的颜色值是一个带有alpha通道的HSL颜色值的延伸 - 指定对象的透明度。
指定HSLA颜色值:HSLA(色调,饱和度,亮度,α),α是Alpha参数定义的不透明度。 Alpha参数是一个介于0.0(完全透明)和1.0(完全不透明)之间的参数。

尽管半透明颜色很受欢迎,但人们对其使用更多集中于背景上。

假设我们想给一个容器设置一层白色背景和一道半透明白色边框,body 的背景会从它的半透明边框透上来。我们最开始的尝试可能是这样的:

border: 10px solid hsla(0,0%,100%,.5); 
background : white;

结果如下,边框不见了:

解决方案

默认状态下,背景会延伸到边框的区域下层,结合下面绿色背景虚线边框的图比较好理解:

这就可以解释我们遇到的难题:body 的背景并没有从内部 content 的半透明的白色半透明边框处透上来,而是在半透明白色边框处透出了这个容器自己的纯白实色背景,这实际上得到的效果跟纯白实色的边框看起来完全一样。

如果要解决这个问题,可以使用 CSS 3 中的 background-clip 属性来进行调整。该属性初始值为 border-box,意味着背景会被元素的 border box(边框的外沿框)裁切掉,如果设置为 padding-box,这样浏览器就会用内边距的外沿来把背景裁切掉。

我们修改代码如下:

border: 10px solid hsla(0,0%,100%,.5); 
background : white; 
background-clip : padding-box;

效果如下:

2.多重边框

难题

多重边框的实现很长时间内都需要各种丑陋的 hack,比如使用多个元素来模拟多重边框。在 CSS 代码层面对多重边框进行灵活的调整看起来非常困难。

解决方案

  1. box-shadow 方案

    box-shadow 可以接受 6 个参数,如下表所示:

    当我们将 spread 属性设为正值,h-shadow、v-shadow 和 blur 均设置为 0,得到的“投影”就像一道实线边框:

    background : yellowgreen; 
    box-shadow : 0 0 0 10px #655;
    

    效果如下:

    使用 border 也可以实现同样的效果,但 box-shadow 的优势在于:它支持逗号分隔语法,这意味着其可以创建任意数量的投影,所以也就可以创建任意数量的边框。这里在上面的基础上添加一道边框:

    background: yellowgreen;
    box-shadow: 0 0 0 10px #655, 0 0 0 15px deeppink;
    

    效果如下:

    有一处需要注意,box-shadow 是层层叠加的,因此需要按此规律调整 spread 值。比如,上面第一层边框的 spread 值为 10 px,则宽度为 10 px;如果第二层边框的宽度需要设置为 5px,则第二个 box-shadow 的spread 值需要设置为 (10 + 5)即 15 px。

    注:

    • 投影的行为跟边框不完全一致,因为它不会影响布局,而且也不会受到 box-sizing 属性的影响。如有需要,可以通过内边距或外边距(这取决于投影是内嵌还是外扩的)来额外模拟出边框所需要占据的空间。
    • 使用 box-shadow 创建的边框,其不会响应鼠标事件, 比如悬停或点击。如果需要使其响应鼠标事件,则可以添加 inset 关键字,使得 box-shadow 创建的边框出现在内圈,然后留出额外的内边距来填补这些空隙。
  2. outline 方案

    此方案适用于只创建两层边框的情况。第一层边框使用 border 属性创建,第二层边框使用 outline 属性创建。该方案非常灵活。先看一下 outline 属性可以接受的参数:

    对于上面用 box-shadow 实现的两层边框,用 outline 实现的方式如下:

    background : yellowgreen; 
    border : 10px solid #655; 
    outline : 5px solid deeppink;
    

    outline 可以和 outline-offset 配合使用,后者用来控制 outline 和元素边缘之间的距离,该属性可以接受负值,对一层 dashed (虚线)outline 使用负的 outline-offset 后,可以得到简单的缝边效果:

    注:

    • outline 只适用于双层边框
    • outline 边框不一定会贴合 border-radius 属性产生的圆角。这种行为被 CSS 工作组认为是一个 bug,因此未来可能会改为贴合 border- radius 圆角。

3. 灵活的背景定位

难题

要求针对容器某个角对背景图片做偏移定位,如右下角。在 CSS 2.1 中,只能指定距离左上角的偏移量,或者靠齐到其他三个角。但是如果希望图片和容器的边角之间能留出一定的空隙,此时便比较难解决。

解决方案

  1. 传统方法

    • 对于固定尺寸的容器,可以基于它自身的尺寸以及我们期望它距离右下角的偏移量,计 算出背景图片距离左上角的偏移量,然后再把计算结果设置给 background- position 。
    • 对于尺寸不固定的容器,上述方案无效。
  2. background-position 的扩展语法方案

    background-position 在 CSS 3 中得到扩展,其允许我们指定背景图片距离任意角的偏移量,只要我们在偏移量前指定关键字,比如:

    background : url(code-pirate.svg) no-repeat #58a; 
    background-position : right 20px bottom 10px;
    

    以上代码表示让背景图片跟右边缘保持 20 px 的偏移量,同时跟底边保持 10px 的偏移量。

    对于不支持 background-position 扩展的浏览器,应该为其提供回退方案,在 background 中指定老式的 right bottom 简写值:

    background : url(code-pirate.svg) no-repeat bottom right #58a; 
    background-position : right 20px bottom 10px;
    
  3. background-origin 方案

    对于 background-position: top left 这样的代码,这里的 top left 是相对于元素的 padding box 的左上角。现在我们使用 CSS 3 新增的 background-origin 属性可以指定矩形框是 content-box、padding-box 还是 border-box。比如:

    padding: 10px; 
    background: url("code-pirate.svg") no-repeat #58a bottom right; /* 或 100% 100% */ 
    background-origin: content-box ;
    

    当设置 background-origin 为 content-box 时,background-position 属性中使用的边角关键字将会以内容区的边缘作为基准(也就是说,此时背景图片距离边角的偏移量就跟内边距保持一致了)。

  4. calc() 方案

    直接上代码:

    background : url("code-pirate.svg") no-repeat; 
    background-position : calc(100% - 20px) calc(100% - 10px);
    

    以上代码的效果也是让背景图片跟右边缘保持 20 px 的偏移量,同时跟底边保持 10px 的偏移量。

    注:在 calc() 函数内部的 - 和 + 运算符的两侧各加 一个空白符,否则会产生解析错误。此举是为了向前兼容: 未来,在 calc() 内部可能会允许使用关键字, 而这些关键字可能会包含连字符(即减号)。

4. 边框内圆角

难题

有时我们需要一个容器,只在内侧有圆角,而边框或描边的四个角在外 部仍然保持直角的形状,如下图所示:

使用两个元素可以实现上述效果,并且很简单:

<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 : tan; 
border-radius : .8em; 
padding: 1em; 
box-shadow : 0 0 0 .6em #655; 
outline : .6em solid #655;

其效果和上面两个元素实现的效果一样。究其原因,正如多重边框一节对 outline 属性介绍时提到的,其并不会贴合 border-radius 属性产生的圆角,但是 box-shadow 会。二者叠加到一起,box-shadow 会刚好填补描边和容器圆角之间的空隙。以下两张图分别是单独使用 outline 和 box-shadow 属性的效果:

我们为 box-shadow 属性指定的扩张值并不一定等于描边的宽度,我们只需要指定一个足够填补“空隙”的扩张值就可以了。这个值具体是多少?看下图:

当圆角半径为 r 时,从圆角的圆心到描边顶角的长度就是 r√2,这意味着投影的扩张半径 spread 值不能小于 r√2 - r = (√2 - 1)r。同时,该值还需要比描边的宽度值小。

5. 条纹背景

难题

目前在网页中实现条纹图案的方式非常繁琐,并且效果往往不够理想。

解决方案

  1. 水平条纹

    使用 linear-gradient 属性可以创建基本的垂直渐变,比如:

    <!--下图一-->
    background : linear-gradient(#fb3, #58a);
    
    <!--下图二-->
    background : linear-gradient(#fb3 20%, #58a 80%);
    
    <!--下图三-->
    background : linear-gradient(#fb3 40%, #58a 60%);
    

    对应效果:

    可以发现,当拉近两个色标时,渐变区域越来越窄。不难想象,如果把两个色标重合在一起,得到的就是两条水平条纹。以下是理论依据:

    “如果多个色标具有相同的位置,它们会产生一个无限小的过渡区域, 过渡的起止色分别是第一个和最后一个指定值。从效果上看,颜色会在那 个位置突然变化,而不是一个平滑的渐变过程。”

    ——CSS 图像(第三版)(http://w3.org/TR/css3-images)

    background : linear-gradient(#fb3 50%, #58a 50%);
    

    对应效果:

    在此基础上,我们可以进行一些再加工:

    <!--可以通过background-size来调整尺寸-->
    <!--背景默认重复平铺,所以整个容器被两条条纹填满-->
    <!--如下图一-->
    background : linear-gradient(#fb3 50%, #58a 50%); 
    background-size : 100% 30px;
    
    <!--还可以用相同的方法来创建不等宽的条纹,只需调整色标的位置值即可-->
    <!--如下图二-->
    background : linear-gradient(#fb3 30%, #58a 30%); 
    background-size : 100% 30px;
    

    针对上面的代码,还可以有改进方案,理论依据如下:

    “如果某个色标的位置值比整个列表中在它之前的色标的位置值都要 小,则该色标的位置值会被设置为它前面所有色标位置值的最大值。”

    ——CSS 图像(第三版)(http://w3.org/TR/css3-images)

    所以,如果我们把第二个色标的位置值设置为 0,那它的位置就总是会被浏览器调整为前一个色标的位置值。我们可以把原先这样的代码

    background : linear-gradient(#fb3 30%, #58a 30%);
    

    改写成这样:

    background : linear-gradient(#fb3 30%, #58a 0);
    

    如果要创建超过两条颜色的条纹,也很容易。以下是生成三条条纹的代码:

    background : linear-gradient(#fb3 33.3%, #58a 0, #58a 66.6%, yellowgreen 0); 
    background-size : 100% 45px;
    

  2. 垂直条纹

    垂直条纹相比水平条纹,在 linear-gradient 的开头加一个额外参数来标记其渐变方向即可(在水平条纹中我们没有加这个参数,因为其默认值就是 to bottom,也就是水平条纹的效果):

    background : linear-gradient(to right, /* 或 90deg */ 
    #fb3 50%, #58a 0); 
    background-size : 30px 100%;
    

    还需要把 background-size 的值颠倒。

  3. 斜向条纹

    如果按照常规思维,要实现斜向条纹,改变 linear-gradient 的方向和 background-size 的值,貌似就可以搞定。如果要实现一个 45° 的条纹图案,代码这样写:

    background: linear-gradient(45deg,#fb3 50%, #58a 0);
    background-size : 30px 30px;
    

    得到的效果却是:

    失败的原因是我们仅仅把每个背景单元作了渐变旋转,而没有站在背景整体的角度来看问题。来看预想的斜向背景的单个背景单元:

    可以看到,在一个背景单元里,实际上有四条条纹,这样就可以实现条纹无缝衔接:

    background : linear-gradient(45deg, #fb3 25%, #58a 0, #58a 50%, #fb3 0, #fb3 75%, #58a 0); background-size : 30px 30px;
    

    关于条纹变成斜向后为何会看起来变瘦了,实际上是因为背景尺寸设置为 30px 时,产生的条纹宽度将是15/√2 ≈ 10.606 像素。如果需要和原先水平或垂直条纹一样的宽度,在其基础上乘以 √2 即可。

  4. 更好的斜向条纹

    上面的方案在我们想调整斜向角度时会非常不灵活。这里提供一个更好的方案。首先需要了解的是 linear-gradient() 和 radial-gradient() 各还有一个循环式的加强版:repeating-linear-gradient() 和 repeating-radial-gradient()。其工作方式和前两者类似,只有一点不同:色标是无限循环重复的,直到填满整个背景。比如以下代码:

    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, ...);
    

    效果图如下:

    第一个好处就是减少了重复,改动颜色时只需要修改两处。另外一点就是现在是在渐变的色标中指定长度,而不是原来的 background-size。因为这些长度是直接在渐变轴上进行度量的,直接代表了条 纹自身的宽度。所以再也不用进行各种数学计算了。

    当需要改变角度为 60° 时,只需要这样:

    background : repeating-linear-gradient(60deg, #fb3, #fb3 15px, #58a 0, #58a 30px);
    
    1. 灵活的同色系条纹

    当条纹图案由差异不大的颜色组成时,按照上面提供的方法,我们可能需要这样写 CSS:

    background : repeating-linear-gradient(30deg, #79b, #79b 15px, #58a 0, #58a 30px);
    

    如图:

    实际上,在应对这种情况时,我们可以把最深的颜色指定为背景色,同时把半透明白色的条纹叠加在背景色之上来得到浅色条纹:

    background : #58a; 
    background-image : repeating-linear-gradient(30deg, hsla(0,0%,100%,.1), hsla(0,0%,100%,.1)    15px, transparent 0, transparent 30px);
    

6. 复杂的背景图案

7. 伪随机背景

8. 连续的图像边框

请支持正版,购买书籍自行查看。

好吧,其实是我觉得这三章内容 focus 的点太小了,感觉了解一下即可。

好吧,其实是我懒得截图整理了,加上确实 focus 的点太小了,所以就偷个懒啦。

CSS
支付宝扫码打赏 微信打赏

坚持原创技术分享,您的支持将鼓励我继续创作!

扫描二维码,分享此文章

逆葵's Picture
逆葵

花名逆葵,刚刚毕业的北邮 CS 土著一枚。现在淘宝 FED 搬砖。《Vue.js 权威指南》作者之一。学习、思考、沉淀中,向成为顶级 JavaScript 技术栈开发者努力。