0%

CSS3中border-image的草案翻译和应用

有些时候 UI 给切的背景,并不能适配所有长宽比的元素,这个时候就需要一个非常强大的 CSS 属性:border-image,下面先翻译border-image相关的W3C 规范草案,再举例说明如何应用到元素背景中。

草案翻译

开发者可以指定用来替代 border-style 的图像。在这种情况下,border 取自 border-image-source 指定的图像的边缘和角落,这些图像的碎片可以通过各种方式进行切片、缩放和拉伸,以适应边框图像区域的大小。 边框图像属性不影响布局: 盒的布局、内容和环绕内容仅基于 border-widthborder-style 属性的影响。

示例 26:

这个例子创建了一个顶部和底部的 borderborder 中包含大量的橙色钻石,左右 border 是一个单一的拉伸钻石,border 的四个角是不同颜色的钻石。 平铺开的图像如下所示,除了钻石以外,其余部分都是透明的:

这个图像是 81 × 81 像素,被分成 9 个大小相等的部分。 因此,样式规则可以如下:

1
2
3
4
div {
border: double orange 1em;
border-image: url('border.png') 27 round stretch;
}

把上述样式应用到 12×5em 的 DIV 上,结果如下所示:

示例 27:

这个示例展示了一个更复杂的情况,演示了边框图像如何对应到预备的 border-style 并且可以扩展到边框区域之外。边框图像是一个带有四角突出效果的绿色波浪边框:

border-image-source 属性对应的图像,使用 4124pxborder-image-slice 属性分割图像,将图像分成了 9 个部分。

然后,其余的 border 属性相互作用,按照以下方式布局:

上图展示了所有的 border-image 属性的相互作用,并且展示了是否使用 border-image 属性对渲染结果的影响。

在本示例中,即使 border-width12pxborder-image-width 依然会计算为 124px。这个边框图像在 border31px 处开始,并且进入了 margin 区域。如果 border-image 加载失败(或者浏览器不支持 border-images) ,预备渲染将使用绿色双实线样式的 border

示例 28:

注意:边框样式简写“border”的将重置 border-image。 这样可以很容易地关闭或重置所有边框效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
.notebox {
border: double orange;
/* 必须先设置简写 'border',否则'border-image'样式不会显示 */
border-image: url('border.png') 30 round;
/* 但是其他的border属性可以之后设置 */
border-width: thin thick;
}
... .sidebar .notebox {
box-shadow: 0 0 5px gray;
border-radius: 5px;
border: none; /* 关闭所有的边框 */
/* 'border'简写重置了 'border-image' */
}

图像来源:border-image-source 属性

名称 border-image-source
none |image
默认值 none
适用范围 所有元素,除了 border-collapse 为 collapse 的内部 table 元素
继承
百分比 不适用
计算值 关键字 none 或者 image 计算值
动画类型 离散

指定一个图像用于替代 border-style 属性的渲染结果,如果在 border-image-slice 中给定 fill 关键字,则把图像也用作元素的附加图像背景。 如果值为 none 或者图像无法显示(或者属性未时使用) ,则使用 border 样式;否则不绘制元素的 border-style 样式边框,而按照下面部分所述绘制边框图像。

图像切片:border-image-slice 属性

名称 border-image-slice
number|percentage,fill
默认值 100%
适用范围 所有元素,除了 border-collapse 为 collapse 的内部 table 元素
继承
百分比 参考边框图像的大小
计算值 4 个数字或者百分比,如果 fill 指定还需要加上 fill
动画类型 按照计算值来实现

此属性指定图像上、右、下和左边缘向内的偏移量,并将其划分为九个区域:四个角、四条边和一个中间区域。 除非 fill 关键字出现,否则中间图像部分将被丢废弃(视为完全透明)。 (中间部分是在背景上绘制的; )

当四个属性指定了以后,就按照上右下左顺序设置偏移量。如果左偏移缺失,和右偏移相等;下偏移缺失,和上偏移相等;右偏移缺失,和上偏移相等。

  • 百分比

    百分比值与图像的大小有关: 水平偏移量参照图像宽度,垂直偏移量参照图像高度

  • 数字

    数字表示图像中的像素(如果图像是位图)或矢量坐标(如果图像是矢量图)。

  • fill

    fill 关键字如果存在,则保留边框图像的中间部分。 (默认情况下不使用 fill)。

负值是无效的。大于图像尺寸的计算值被解释为 100%

border-image-slice 指定的区域可以重叠。但是如果左右偏移的宽度之和大于等于图像宽度,则上中,下中和最中间位置处的图像为空,与这三个部分指定了非空的透明图像效果相同。上下偏移宽度和大于图像高度时结果是类似的。

如果必须对图像进行大小调整才能确定切片(例如,没有内置大小的 SVG 图像),则使用默认尺寸调整算法设置图像大小,并将边框图像区域作为默认对象尺寸

上图展示了按照 25% 30% 12% 20%切割图像的示意。

绘图范围:border-image-width 属性

名称 border-image-width
number|length-percentage|auto
默认值 1
适用范围 所有元素,除了 border-collapse 为 collapse 的内部 table 元素
继承
百分比 参考边框图像范围的 width/height
计算值 4 个值,每一个都可以是数字,关键字 auto 或长度百分比计算值
动画类型 按照计算值来实现

边框图像是在一个称为边框图像区域的范围内绘制的。 默认情况下,边界与边框盒对应。

border-image-width 的四个值指定用于将边框图像区域分为九个部分的偏移量。 它们分别表示从该区域的上、右、下和左侧向内的距离。 如果左宽度缺失,和右宽度相等; 如果下宽度缺失,和上宽度相等; 如果右宽度缺失,和上宽度相等。 值有以下含义:

  • length-percentage

    百分比参考的是边框图像区域的大小: 水平偏移量参考区域宽度,垂直偏移量参考区域高度。

  • number

    数字表示相应 border-width 计算值的倍数。

  • auto

    如果指定了 auto,则边框图像宽度是相对应图像切片的内部宽度或高度。 如果图像没有所需的内部宽高,那么用相对应的 border-width 计算值来代替。

任何 boder-image-width 值都不允许使用负值。

如果两个相反的 border-image-width 偏移量大到以至于重叠,那么所有 border-image-width 偏移量的使用值都会按比例减少,直到它们不再重叠。

边缘超出:border-image-outset 属性

名称 border-image-outset
length|number
默认值 0
适用范围 所有元素,除了 border-collapse 为 collapse 的内部 table 元素
继承
百分比 不适用
计算值 4 个值,每一个都可以是数字或绝对长度
动画类型 按照计算值来实现

border-image-outset 的四个值用于指定边框图像区域超出边框盒的距离。 它们分别表示从该区域的上、右、下和左侧超出的距离。 如果左距离缺失,和右距离相等;如果下距离缺失,和上距离相等;如果右距离缺失,和上距离相等。 值有以下含义:

  • length

    表示超出距离的长度。负值无效。

  • number

    用相应 border-width计算值的倍数来表示超出距离。负值无效。

注意:即使 border-image-outset 从未引起滚动机制,但超出部分的图像仍然可能被上级元素或视窗给剪掉。

图片平铺:border-image-repeat 属性

名称 border-image-repeat
stretch|repeat|round|space
默认值 stretch
适用范围 所有元素,除了 border-collapse 为 collapse 的内部 table 元素
继承
百分比 不适用
计算值 两个关键词,每个轴一个
动画类型 按照计算值来实现

此属性指定如何缩放和平铺边框图像中的边缘和中间部分的图像。 第一个关键字适用于顶部、中部和底部的水平缩放和平铺,第二个关键字适用于左部、中部和右部的垂直缩放和平铺。 如果缺失第二个关键字,则假定它与第一个关键字相同。 值有以下含义:

  • stretch

    .图像被拉伸以填充区域

  • repeat

    将图像平铺(重复)以填充区域

  • round

    将图像平铺(重复)以填充区域。 如果没有将整个区域填满,则对图像进行重新缩放以使其填满。

  • space

    将图像平铺(重复)以填充该区域。 如果它没有将整个区域填满瓷砖,那么额外的空间将分布在瓦片周围

下面一节给出了缩放和平铺 border-image 部分的确切过程。

绘制边框图像

将由 border-image-source 提供的 border-image 使用 border-image-slice 进行切片后,将生成的 9 个瓦片按 4 个步骤进行缩放、定位和拼接到相应的 border-image 区域:

  1. 关于 border-image-width的缩放

    • 使上和下的两个边缘图像分别与上边缘和下边缘图像区域一样高,同时同比例缩放宽度。

    • 使左和右的两个边缘图像分别与左边缘和右边缘图像区域一样宽,同时同比例缩放高度。

    • 四个角图像被缩放成与它们所属的两个边框图像边缘一样的宽高。

    • 中间图像的宽度按照与顶部图像相同的比例进行缩放,除非该比例为零或无穷大,在这种情况下,底部的缩放比例被替换,如果达不到这一点,则不进行缩放。 中间图像的高度将按照与左图像相同的比例进行缩放,除非该比例为零或无穷大,在这种情况下,右图像的缩放比例将被替换,如果达不到这一点,高度将不进行缩放。

  2. 关于 border-image-repeat的缩放

    • 如果第一个关键字是 stretch,那么顶部、中部和底部的图像将进一步缩放,使其与边框图像区域的中间部分一样宽。 高度不再改变。
    • 如果第一个关键字是 round,那么顶部、中间和底部的图像就会在宽度上调整大小,这样它们的整个数量就恰好适合于边框图像区域的中间部分,与 background-repeat 属性中的 round 完全一样。
    • 如果第一个关键字是 repeatspace,则顶部、中间和底部图像不会进一步更改。
    • 第二个关键字的 stretchroundrepeatspace 的效果是类似的,它们作用于左、中、右图像的高度。
  3. 定位第一块瓦片

    • 如果第一个关键字是 repeat,则顶部、中间和底部的图像将在各自区域中水平居中。 否则,图像被放置在边界图像区域各自部分的左边缘。
    • 如果第二个关键字是 repeat,则左图、中图和右图在各自的区域垂直居中显示。 否则,图像被放置在边界图像区域各自部分的顶部边缘。
  4. 平铺和绘制

    • 然后将图像平铺以填充各自的区域。
    • space 的情况下,任何局部瓦片都被丢弃,并且额外的空间分布在瓦片前、间、后(即平衡第一个瓦片之前的间隙、最后一个瓦片之后的间隙以及瓦片之间的间隙)。 这可能导致边框图像边缘区域是空的。
    • 图像是在与正常 border 相同的层次级别上绘制的:直接绘制在背景图层的上面。
    • 除非为 border-image-source 指定了 fill,否则不绘制中间图像。

边框图像简写:border-image 属性

名称 border-image
border-image-source|| border-image-slice [ / border-image-width / border-image-width ? [ border-image-outset] ?|| border-image-repeat
默认值 见各自属性
适用范围 见各自属性
继承
百分比 不适用
计算值 见各自属性
动画类型 见各自属性

这是设置 border-image-sourceborder-image-sliceborder-image-widthborder-image-outsetborder-image-repeat 的简化属性。 省略的值设置为它们各自的初始值。

对表格的影响

border-image 属性可以应用于 border-collapse 设置为 collapse 的表格和行内表格的 border。但是,此规范没有定义如何渲染这样的图像边框。 尤其是规范没有定义图像边界如何与表格边缘的单元格、行和行组的边界的相互作用(请参见中的边界冲突解决方法)。

预计未来的规范将定义渲染规则。 建议在此之前,浏览器不要将边框图像应用于边框已折叠的表格。

实际应用

首先说明一点,实验中发现 Chrome80 浏览器中的 border-image-width 支持 px 单位,注意1px有时候与倍数1的区别。

普通用法

给出一个 button 的背景图片如下,像素数是 300×128

如果使用普通的 background,应用到 1400×200 的 button 上,由于长宽比不同,就会出现拉伸的情况,代码和结果如下所示:

1
2
3
4
5
6
7
button {
width: 1400px;
height: 200px;
border: none;
background: url(./assets/chinoiserie.png) no-repeat;
background-size: 100% 100%;
}

这个时候最适合 border-image 完成这个任务,首先测量下图片的细节尺寸:

接下来把 background 设置为透明,由于边框图像的横纵宽度都是 25px,所以先给 border 设置 25px 宽度来开启边框。然后设置 border-imageborder-image-source 就是背景图,border-image-slice 上下左右都是 25,并且添加关键字 fill 来填充最中间的瓦片,后面的 border-image-outsetborder-image-widthborder-image-repeat 三个值默认即可。

1
2
3
4
5
6
7
8
9
10
11
button {
width: 1400px;
height: 200px;
background: transparent;
border: 25px solid transparent;
border-image-source: url(./assets/chinoiserie.png);
border-image-slice: 25 fill;
/* border-image-outset: 0;
border-image-width: 1;
border-image-repeat: stretch; */
}

结果如下,button 按钮可以随便改变宽高比也不会使背景拉伸。

升级用法

下面说一些更延伸的使用方式,再给出一个 button 的背景图片如下,像素数为400×128,仍然想把它给1400×200的 button 当背景:

这次的背景图与上一个的区别主要在两个地方,第一个是圆角幅度特别大,已经组成了两个半圆,所以在纵向上没有可以 stretchrepeat 的空间;第二个是右侧有一个圆包着“¥”符号,而且符号比较靠内而不贴近边缘,在横向上左右不对称。

虽然情况稍微复杂,但是强大的 border-image 仍然可以解决,继续分割一下图片:

这次的分割并没有把图片分割成 9 份,而是去掉了中间一行分割成了 6份,半圆的半径是 64px,“¥”符号的整体距离右侧 120px。具体实现的想法就是在横向上让最中间一行的高度为 0,纵向上“¥”符号整体作为最右侧一列边框部分,再使用 border-image-width 将边框向内拉伸合并起来。

第一步先用 CSS 把背景图像分割,注意 border 的宽度设为 2px 做一个基本量:

1
2
3
4
5
6
7
8
button {
width: 1400px;
height: 200px;
background: transparent;
border: 2px solid transparent;
border-image-source: url(./assets/money.png);
border-image-slice: 64 120 64 64;
}

此时的效果如下,可以发现中间一行已经为空,上下是宽度为 2pxborder

第二步,使用 border-image-width 属性,将边框向内部入侵,注意,由于分割的时候是按照 64 120 64 64,所以向内放大入侵时也应该按照相同的比例,一直放大到上下边框图像合并(64×2px,120×2px,64×2px,64×2px,规范中规定上下边框图像重叠后会自动调整为不重叠),伪造出一种背景图片填充的效果。

1
2
3
4
5
6
7
8
9
10
11
button {
width: 1400px;
height: 200px;
background: transparent;
border: 2px solid transparent;
border-image-source: url(./assets/money.png);
border-image-slice: 64 120 64 64;
border-image-width: 64 120 64 64;
/* border-image-outset: 0;
border-image-repeat: round; */
}

最终效果如下:

👆 全文结束,棒槌时间到 👇