> 原文出处:http://www.w3cplus.com/css3/css-secrets/continuous-image-borders.html
## 问题
有时候,我们想将一些图案或图片添加为边框,而不是背景。比如,在下图中所示的元素就拥有一个装饰性的边框,边框内是一个被限制在边框区域的图片。此外,我们还希望图片可以覆盖整个边框区域。那么我们怎样使用 CSS 达到这一目的呢?
![图片边框](https://box.kancloud.cn/2015-10-01_560cddee01bf9.png "图片边框")
*图注:图片被用来装饰各种高度的边框*
此时,也许在你的内心会响起一个响亮的声音:“border-image,border-image,我们可以使用 `border-image`,它可以完美的解决这个问题!”年轻人,做事不要太急躁。让我们重新回忆一下 `border-image` 的解析机制:它实际上将背景分成了九块,然后分别将各个块作用到了元素的边角上。下图就是一个视觉原理图。
![图片边框](https://box.kancloud.cn/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://box.kancloud.cn/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://box.kancloud.cn/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://box.kancloud.cn/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://box.kancloud.cn/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://box.kancloud.cn/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://box.kancloud.cn/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://box.kancloud.cn/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)属性模拟出图片边框效果。