笔记--《CSS揭秘》第2章

301 阅读17分钟

半透明边框

默认情况下,背景会延伸到边框所在的区域下层。这一点很容易验证,给一个有背景的元素应用一道老土的虚线边框,就可以看出来(参见图 2-3)

image.png

  • 解决:可以通过 background-clip 属性来调整上述默认行为所带来的不便。这个属性的初始值是 border-box,意味着背景会被元素的 border box(边框的外沿框)裁切掉。如果不希望背景侵入边框所在的范围,我们要做的就是把它的值设为 padding-box,这样浏览器就会用内边距的外沿来把背景裁切掉。
border: 10px solid hsla(0,0%,100%,.5);
background: white;
background-clip: padding-box;

image.png

多重边框

box-shadow 的基本用法

box-shadow 方案

box-shadow的第四个参数(称作“扩张半径”),通过指定正值或负值,可以让投影面积加大或者减小。

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

image.png

相比border来说,box-shadow支持逗号分隔语法,可以创建任意数量的投影。甚至还可以在这些“边框”的底下再加一 层常规的投影

background: yellowgreen;
box-shadow: 0 0 0 10px #655,
0 0 0 15px deeppink,
0 2px 5px 15px rgba(0,0,0,.6);

image.png

注意事项:

  • 投影的行为跟边框不完全一致,不会影响布局,也不会受box-sizing属性的影响

  • 上述方法所创建出的假“边框”出现在元素的外圈,并不会响应鼠标事件,比如悬停或点击。想要响应鼠标事件,可以给box-shadow 属性加上 inset 关键字,来使投影绘制在元素的内圈

outline 方案

在某些情况下,你可能只需要两层边框,那就可以先设置一层常规边框,再加上 outline(描边)属性来产生外层的边框。这种方法的一大优点在于边框十分灵活,不像box-shadow 方案只能模拟实线边框。

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

image.png

描边的另一个好处在于,你可以通过 outline-offset 属性来控制它跟元素边缘之间的间距,这个属性甚至可以接受负值。

image.png

注意事项:

  • 只适用于双层“边框”的场景,因为 outline 并不能接受用逗号分隔的多个值。
  • 边框不一定会贴合 border-radius 属性产生的圆角,因此如果元素是圆角的,它的描边可能还是直角的。
  • 根据 CSS 基本 UI 特性(第三版)规范所述,“描边可以不是矩形”。尽管在绝大多数情况下,描边都是矩形的,但如果你想使用这个方法,请切记:最好在不同浏览器中完整地测试最终效果。

image.png

灵活的背景定位

background-position 的扩展语法方案

CSS 背景与边框(第三版)中,background-position 属性已经得到扩展,它允许我们指定背景图片距离任意角的偏移量,只要我们在偏移量前面指定关键字。举例:

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

image.png image.png

考虑到有的浏览器不支持扩展语法,需要一个回退方案:把老套的bottom right 定位值写进 background 的简写属性中。

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

background-origin 方案

在给背景图片设置距离某个角的偏移量时,有一种情况极其常见:偏移量与容器的内边距一致。background-position 的扩展语法方案,需要计算减去内边距的值。当每次改变内边距,要更改对应的background-position,代码不够DRY

那么有个简单的方法:让它自动地跟着我们设定的内边距走,不用另外声明偏移量的值,这就涉及元素的三个矩形框了。

每个元素身上都存在三个矩形框(参见图 2-14):border box(边框的外沿框)、padding box(内边距的外沿框)和 content box(内容区的外沿框)。 默认情况下,background-position 是以 padding box 为准的,这样边框才不会遮住背景图片。在背景与边框(第三版)中,我们得到了一个新的属性 background-origin,可以用它来改变这种行为。

image.png

padding: 10px;
background: url("code-pirate.svg") no-repeat #58a
bottom right; /* 或 100% 100% */
background-origin: content-box;/* 以内容区的边缘作为基准,此时背景图片距离边角的偏移量就跟内边距保持一致了 */

calc() 方案

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

calc() 函数实现把背景图片定位到距离底边10px且距离右边20px 的位置。

background: url("code-pirate.svg") no-repeat;

background-position: calc(100% - 20px) calc(100% - 10px);

边框内圆角

背景知识:box-shadow,outline,“多重边框”

要如何实现只在内侧有圆角,而边框或描边的四个角在外部仍然保持直角的形状(如图)?第一反应是不是想到,直接用两个元素实现这个效果,那用一个元素可以实现吗?怎样实现呢? image.png image.png

这里并不是说一个元素实现出来就是优于两个元素,两个元素方案会更加灵活一些,允许我们充分运用背景的能力(如:“边框”不只是纯色的,而是要加一层淡淡的纹理)。不过,如果只需要达成简单的实色效果,那我们就还有另一条路可走,只需用到一个元素。

background: tan;
border-radius: .8em;
padding: 1em;
box-shadow: 0 0 0 .6em #655;
outline: .6em solid #655;

image.png image.png image.png

这个方案中,描边跟圆角之间会有空隙,就需要box-shadow去填充,但是指定的扩张值并不一定等于描边的宽度,我们只需要指定一个足够填补“空隙”的扩张值就可以了。事实上,指定一个等于描边宽度的扩张值在某些浏览器中可能会得到渲染异常,因此推荐一个稍小些的值。这又引出了另一个问题:到底多大的投影扩张值可以填补这些空隙呢?

image.png

这就要用勾股定理来计算直角三角形各边的长度。勾股定理表明,如果直角边分别是 a 和 b,则斜边(正对着直角的最长边)等于a2+b2\sqrt{a^2+b^2},当两条直角边的长度相等时,这个算式会演化为2a2=a2\sqrt{2a^2}=a\sqrt{2}。 在我们的例子中,border-radius 是 .8em,那么最小的扩张值就是0.8(21)0.33137085em0.8(\sqrt{2}-1)\approx0.331 370 85 em 。我们要做的就是把它稍微向上取个整,把 .34em 设置为投影的扩张半径。为了避免每次都要计算,你可以直接使用圆角半径的一半,因为 21<0.5\sqrt{2}-1<0.5

但是该计算过程揭示了这个方法的另一个限制:为了让这个效果得以达成,扩张半径需要比描边的宽度值小,但它同时又要比(21)r(\sqrt{2}-1)r 大(这里的 r 表示 border-radius)。这意味着,如果描边的宽度比(21)r(\sqrt{2}-1)r 小,那我们是不可能用这个方法达成该效果的。

条纹背景

背景知识:CSS 线性渐变,background-size

水平条纹

实现方式:使用linear-gradient设置颜色并且设置相同的起始位置,缩小高度,使背景重复平铺。具体如下:

image.png image.png

  • 等宽
background: linear-gradient(#fb3 50%, #58a 50%);
background-size: 100% 30px;

image.png

  • 不等宽
background: linear-gradient(#fb3 30%, #58a 30%);
background-size: 100% 30px;

让代码更加的DRY,可以将第二色标的位置值设为0,那么它的位置值会被浏览器设置与前一个色标值相同的值。

“如果某个色标的位置值比整个列表中在它之前的色标的位置值都要小,则该色标的位置值会被设置为它前面所有色标位置值的最大值。”——CSS 图像(第三版)(w3.org/TR/css3-ima…

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

image.png

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

image.png

垂直条纹

实现方式与水平条纹几乎一致,差别主要在于要指定个方向

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

image.png

斜向条纹

垂直条纹水平条纹的区别在于指定方向的参数和background-size 的值,那么是不是改变 background-size 的值和渐变的方向就可以实现斜向条纹

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

但是最终出来的效果不是斜向条纹,下图:

image.png

为什么会出现这种效果呢?原因在于我们只是把平铺图案中的每个基本单元内部的渐变旋转了 45°,而不是把整个重复的背景都旋转了。

image.png image.png

由图上所示,条纹的宽度实际上是直角三角形的高如果想让条纹的宽度变化为我们原本想要的 15px,就需要把 background-size 指定为215242.4264068712*15\sqrt{2}\approx42.426 406 871像素:

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

为了方便直接取近似值42px,区别不大。

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

image.png image.png

更好的斜向条纹

如果我们不要45°的条纹,设置60°、48°.....,那么每次都要去计算,而且不一定能实现出来

image.png

要做到更好的实现方式,就要用到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, ...);

image.png

实现斜条纹效果:

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

image.png image.png

改变角度就可以获得60°的斜条纹

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

最好用前面的方法来实现水平或垂直的条纹,而用这种方法来实现斜向条纹。

灵活的同色系条纹

在大多数情况下,我们想要的条纹图案并不是由差异极大的几种颜色组成的,这些颜色往往属于同一色系,只是在明度方面有着轻微的差异。

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

image.png

这种写法不仅没有体现出两种颜色之间的关系,如果需要修改条纹的主色调,就要修改四个地方。幸运的是,还有一种更好的方法:不再为每种条纹单独指定颜色,而是把最深的颜色指定为背景色,同时把半透明白色的条纹叠加在背景色之上来得到浅色条纹:

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

现在只需要修改一个地方就可以改变所有颜色了,对于那些不支持CSS 渐变的浏览器来说,这里的背景色还起到了回退的作用。

复杂的背景图案

背景知识:CSS 渐变,“条纹背景”

用 CSS 渐变来创建任何种类的几何图案几乎都是可能的,只不过有时这种方法不太实际。CSS 图案可以算是一个值得使用 CSS 预处理器来减少代码冗余的案例,因为最终图案越复杂,相应的代码就会变得越来越不DRY。

如果要统计你的 CSS 代码的文件体积,可以把代码粘贴到bytesizematters.com

网格

思想思路把多个渐变图案组合起来,让它们透过彼此的透明区域显现。按照这个思路,我们首先想到的可能就是把水平和垂直的条纹叠加起来,从而得到各种样式的网格。

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;

image.png

  • 在某些情况下,我们希望网格中每个格子的大小可以调整,而网格线条的粗细同时保持固定。如:类似图纸辅助线的网格
background: #58a;
background-image:
 linear-gradient(white 1px, transparent 0),
 linear-gradient(90deg, white 1px, transparent 0);
background-size: 30px 30px;

image.png

上面这种写法,主色调在这里也起到了回退颜色的作用;当需要改变网格的尺寸、线宽或者任何颜色时,我们可以很容易地找到需要编辑的地方。在改变图案的任何一个要素时,只需修改一到两个值;代码很简短,只有四行。

  • 甚至可以把两幅不同线宽、不同颜色的网格图案叠加起来,得到一个更加逼真的蓝图网格。 image.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;

image.png

波点

使用径向渐变实现波点图案

background: #655;
background-image: radial-gradient(tan 30%, transparent 0);
background-size: 30px 30px;

image.png image.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;

image.png image.png

注意,为了达到效果,第二层背景的偏移定位值必须是贴片宽高的一半

  • 可以使用预处理器简化代码

image.png

棋盘

在 CSS 中创建棋盘图案是可能的,只不过实现过程可能比我们想像中的要“绕”一些。这里的窍门在于用两个直角三角形来拼合出我们想要的方块

  • 创建三角形
background: #eee;
background-image:
 linear-gradient(45deg, #bbb 50%, transparent 0);
background-size: 30px 30px;

image.png

  • 把这些三角形的直角边缩短到原来的一半,从而只占据贴片面积的 1/8。再把色标的顺序反转,就可以创建相反方向的三角形
background: #eee;
background-image:
 linear-gradient(45deg, transparent 75%, #bbb 0);
background-size: 30px 30px;

image.png

  • 把它们组合在一起
background: #eee;
background-image:
 linear-gradient(45deg, #bbb 25%, transparent 0),
 linear-gradient(45deg, transparent 75%, #bbb 0);
background-size: 30px 30px;

image.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;

image.png image.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;

image.png

优化方案:可以用预处理器或者SVG来实现

image.png

image.png image.png image.png

Bennett Feely 的图案库(bennettfeely.com/gradients)

image.png

伪随机背景

背景知识:CSS 渐变,“条纹背景”,“复杂的背景图案”

重复平铺的几何图案很美观,但看起来可能有一些呆板。其实自然界中的事物都不是以无限平铺的方式存在的。即使重复,也往往伴随着多样性和随机性。

模拟条纹的随机性:把这组条纹从一个平面拆散为多个图层:一种颜色作为底色,另三种颜色作为条纹,然 后再让条纹以不同的间隔进行重复平铺。

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%;
    }

image.png

这样的结果显明更有随机的感觉;但如果仔细观察的话,仍然可以看出图案每隔 240px 就会重复一次。这里贴 片的尺寸实际上就是所有 background-size 的最小公倍数,而 40、60 和 80的最小公倍数正是 240。

根据这个逻辑,要让这种随机性更加真实,我们得把贴片的尺寸最大化。为了让最小公倍数最大化,这些数字最好是“相对质数”。在这种情况下,它们的最小公倍数就是它们的乘积。

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%;

这个技巧被 Alex Walker 定名为“蝉原则”,他最先提出了通过质数来增加随机真实性的想法。请注意这个方法不仅适用于背景,还可以用于其他涉及有规律重复的情况。

  • 在照片图库中,为每幅图片应用细微的伪随机旋转效果时,可以使用多个 :nth-child(a) 选择符,且让 a 是质数。
  • 如果要生成一个动画,而且想让它看起来不是按照明显的规律在循环时,我们可以应用多个时长为质数的动画。

连续的图像边框

背景知识:CSS 渐变,基本的 border-image,“条纹背景”,基本的 CSS 动画

主要的思路就是在背景图片之上,再叠加一层纯白的实色背景。为了让下层的图片背景透过边框区域显示出来,我们需要给两层背景指定不同的 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;
background-origin: border-box;

image.png

  • 如果不设置background-origin设置为 border-box,会产生下面怪异的拼接效果,原因是 background-origin 的默认值是 padding-box,因此,图片的显示尺寸不仅取决于 padding box 的尺寸,而且被放置在了 padding box 的原点(左上角)。我们看到的实际上就是背景图片以平铺的方式蔓延到 border box 区域的效果。 image.png
生成一种老式信封样式的边框:
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;

这个效果也可以通过 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);

image.png

创建出各种特殊样式的虚线框:不管是为虚线线段指定不同的颜色,还是自定义线段的长度和间隙的长度
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;

image.png

补充: border-image,原理基本上就是九宫格伸缩法:把图片切割成九块,然后把它们应用到元素边框相应的边和角。 image.png

小结

  • 半透明边框:修改background-clip值为padding-box

image.png

  • 多重边框:box-shadow或者outline

image.png image.png

  • 灵活的背景定位:background-position 的扩展语法或background-origincalc()

image.png image.png image.png

  • 边框内圆角: border-radius box-shadow outline

image.png

  • 条纹背景:CSS 线性渐变repeating-linear-gradientbackground-size

image.png

image.png

image.png

image.png

  • 复杂的背景图案:CSS 渐变“条纹背景”

image.png image.png image.png

  • 伪随机背景:CSS 渐变“条纹背景”“复杂的背景图案”最小公倍数 蝉原则

image.png

  • 连续的图像边框:background-size background-clip background-origin

image.png