border-image 如何与 linear-gradient 一起使用?

How does border-image work with linear-gradient?

我想了解 border-image-slice 在渐变边框图像的情况下是如何工作的。在规范中写道 border-image-slice 的值可以是一个数字

Represents an edge offset in pixels for raster images and coordinates for vector images. For vector images, the number is relative to the element's size, not the size of the source image, so percentages are generally preferable in these cases.

examples from CSS-tricks article 中,边框图像设置如下:

border-image: repeating-linear-gradient(45deg, 
        #000, #000 1.5%, 
        transparent 1.5%, transparent 5%) 80;

因此,根据规范 80 是相对于 div 的大小(宽度:26em;高度:23em;)。但是我还是不明白这是什么意思。当我改变 div 的宽度或高度时,边框图像并没有改变它的外观。但是,当我更改 border-image-slice 或 border width 时,外观会发生显着变化。所以看起来数字 80 和 5em 的边框宽度之间存在相关性。 (边框看起来与数字 40 相同,边框宽度为 2.5em,16 为 1em,等等)。

我的问题是数字 80 是如何计算的,这意味着给定 div 和梯度的切片过程是什么? (草图将不胜感激) 80 似乎不是 px、em 或 %,因为当我添加这些单位时,外观会发生变化。

完整代码在这里:

div {
 box-sizing: border-box;
 position: relative;
 border: solid 5em #000;
 border-image: repeating-linear-gradient(45deg, 
   #000, #000 1.5%, 
   transparent 1.5%, transparent 5%) 80;
 padding: 2em;
 width: 26em; height: 23em;
 background: linear-gradient(to right bottom, 
   #e18728, #4472b9);
 background-size: 50% 50%; 
}
<div></div>

在下一个示例中,我使用 px 而不是 em,因为我认为它更清晰。

这是用于边框图像的图像。

div{ width: 416px; height: 368px;
 background:repeating-linear-gradient(45deg, 
   #000, #000 1.5%, 
   transparent 1.5%, transparent 5%);
}
<div></div>

这张图片将被切成 9 个正方形,类似于网格。

图片来自这篇文章:border-image-slice

如果 border-image-slice 的值为 80,这意味着偏移量为 80,即 C1、C2、C3 和 C4 大小为 80/80。所有 C 切片都用于边界图像的角。 E1,E2,E3,E4是用来画边的。

如果您使用的是 208 或 50% 而不是 80,则边框图像将有角但没有边缘,因为边缘没有任何剩余。

接下来是一个演示,您可以在其中看到用于绘制边框图像的图像切片的演变。我已将 div 的宽度更改为 300,因为我想同时看到带有边框图像 的 div 和使用的图像 border 一个挨着一个。在这种情况下,边框图像的边缘在 border-image-slice:150;

处消失

itr.addEventListener("input",()=>{
 let v = itr.value;
 border.style.borderImageSlice = v;
 itrspan.innerHTML = v;
 let d = `M${v},0v300M${300-v},300v-300M0,${v}h300M300,${300-v}h-300`
 thePath.setAttributeNS(null,"d",d)
})
div{display:inline-block;}

#border {
 box-sizing: border-box;
 position: relative;
 border: solid 5em #000;
 border-image: repeating-linear-gradient(45deg, 
   #000, #000 1.5%, 
   transparent 1.5%, transparent 5%);
 border-image-slice:80;
 padding: 2em;
 width: 300px; height: 300px; 
}

#image{
 width: 300px; height: 300px;
 background: repeating-linear-gradient(45deg, 
   #000, #000 1.5%, 
   transparent 1.5%, transparent 5%);}



input{width:300px;}
<input id="itr" type="range" min="0" max="300" value="80" ><span id="itrspan">80</span>
<br>


<div id="border"></div>
<svg id="image" viewBox="0 0 300 300">
 
<path id="thePath" fill="none" stroke="red" d="M80,0v300M220,300v-300M0,80h300M300,220h-300" />
</svg>

TL;DR

使用渐变时,图像的大小就是元素的大小。 border-image-width 将定义我们将放置切片的 9 个区域(如果未定义,则使用 border-width)。 border-image-slice 将考虑初始图像来创建切片。无单位值被视为像素值,百分比值根据元素的大小进行解析。

为了获得完美的结果,我们应该切片等于区域,为此我们需要border-image-slice等于border-image-width(或 border-width) 在没有单位的情况下使用。使用百分比,计算值应该相同。

在你的情况下 80 在切片中意味着 80px 并且你有一个 5em 的边框,即 5x16px = 80px.


举个简单的例子。

div {
  width: 100px;
  height: 100px;
  display: inline-block;
  border: 10px solid transparent;
}

div.box {
  background: repeating-linear-gradient(45deg, #000, #000 5px, transparent 5px, transparent 10px) border-box, red;
}

div.border {
  border-image: repeating-linear-gradient(45deg, #000, #000 5px, transparent 5px, transparent 10px) 50 fill;
  border-image-width: 50px;
  background: red;
}
<div class="box"></div>

<div class="border"></div>

在上面,我尝试使用不同的技术(背景和边框)创建两个具有相同输出的 div。请注意,在第二个示例中,我如何使用关键字 fill 并指定了与边框宽度不同的 border-image-width 并使用了等于该边框宽度的切片。

请注意,切片中的 50 在此处被视为像素,因为我们正在处理 non-vector 图像(渐变)。

Numbers represent pixels in the image (if the image is a raster image) or vector coordinates (if the image is a vector image). ref

让我们删除 fill 关键字:

div {
  width: 100px;
  height: 100px;
  display: inline-block;
  border: 10px solid transparent;
}

div.box {
  background: repeating-linear-gradient(45deg, #000, #000 5px, transparent 5px, transparent 10px) border-box, red;
}

div.border {
  border-image: repeating-linear-gradient(45deg, #000, #000 5px, transparent 5px, transparent 10px) 50;
  border-image-width: 50px;
  background: red;
}
<div class="box"></div>

<div class="border"></div>

The fill keyword, if present, causes the middle part of the border-image to be preserved. (By default it is discarded, i.e., treated as empty.) ref

边框图片默认不画在中间,只画在边框。我们可以从示例中清楚地看到我们在每一侧都有 50px,我们的自定义边框同样由 border-image-width.

定义

如果我们不指定 border-image-width,则默认值为 1,这意味着:

Numbers represent multiples of the corresponding computed border-width.

所以我们要么明确指定 border-image-width,要么简单地使用 border-width 作为参考。在大多数情况下只需要 border-width 因为在大多数情况下我们只想覆盖边界区域而不是更多。

现在切片会将图像分成 9 部分:

This property specifies inward offsets from the top, right, bottom, and left edges of the image, dividing it into nine regions: four corners, four edges and a middle

ref

以下步骤可以更好地展示我们的示例是如何完成的:

div {
  width: 100px;
  height: 100px;
  border: solid 10px transparent;
  display: inline-block;
  position: relative;
}

div:before {
  content: "";
  position: absolute;
  top: -10px;
  left: -10px;
  right: -10px;
  bottom: -10px;
  background:
    linear-gradient(green,green) left 0 top    50px/100% 1px no-repeat,
    linear-gradient(green,green) left 0 bottom 50px/100% 1px no-repeat,
    linear-gradient(green,green) top 0 left  50px/1px 100% no-repeat,
    linear-gradient(green,green) top 0 right 50px/1px 100% no-repeat;
}

div.box {
  background: repeating-linear-gradient(45deg, #000, #000 5px, transparent 5px, transparent 10px) border-box, red;
}

div.border {
  border-image: repeating-linear-gradient(45deg, #000, #000 5px, transparent 5px, transparent 10px) 50;
  border-image-width: 50px;
  background: red;
}
<div class="box"></div>

<div class="border"></div>

左图是原图,我们把它分成了9个部分,然后我们把每一个都放在右图的9个区域中。中间一个是空的,因为我们没有使用fill。在此示例中,我们不会注意到任何东西,因为切片适合区域。

现在让我们将切片缩小到 25:

div {
  width: 100px;
  height: 100px;
  border: solid 10px transparent;
  display: inline-block;
  position: relative;
}

div.box:before {
  content: "";
  position: absolute;
  top: -10px;
  left: -10px;
  right: -10px;
  bottom: -10px;
  background:
    linear-gradient(blue,blue) left 0 top    25px/100% 1px no-repeat,
    linear-gradient(blue,blue) left 0 bottom 25px/100% 1px no-repeat,
    linear-gradient(blue,blue) top 0 left  25px/1px 100% no-repeat,
    linear-gradient(blue,blue) top 0 right 25px/1px 100% no-repeat;
}

div.border:before {
  content: "";
  position: absolute;
  top: -10px;
  left: -10px;
  right: -10px;
  bottom: -10px;
  background: 
    linear-gradient(green, green) left 0 top 50px/100% 1px no-repeat, 
    linear-gradient(green, green) left 0 bottom 50px/100% 1px no-repeat, 
    linear-gradient(green, green) top 0 left 50px/1px 100% no-repeat, 
    linear-gradient(green, green) top 0 right 50px/1px 100% no-repeat;
}

div.box {
  background: repeating-linear-gradient(45deg, #000, #000 5px, transparent 5px, transparent 10px) border-box, red;
}

div.border {
  border-image: repeating-linear-gradient(45deg, #000, #000 5px, transparent 5px, transparent 10px) 25;
  border-image-width: 50px;
  background: red;
}
<div class="box"></div>

<div class="border"></div>

这有点棘手,但同样的逻辑适用。从左边的图像中,我们从每一侧使用 25px 进行切割,得到我们将放入右边的 9 个部分,其中 border-width 仍然相同 (50px)。你可以清楚地注意到角落里的部分是如何简单地缩放而边缘是扭曲的。

在每个角落,我们在 50px 50px 区域内使用 25px 25px 图像,例如在顶部边缘,我们在 10px 50px 区域内使用 60px 25px 图像面积

您还可以为每一侧定义不同的值,如下所示:

div {
  width: 100px;
  height: 100px;
  border: solid 10px transparent;
  display: inline-block;
  position: relative;
}

div.box:before {
  content: "";
  position: absolute;
  top: -10px;
  left: -10px;
  right: -10px;
  bottom: -10px;
  background: 
    linear-gradient(blue, blue) left 0 top 20px/100% 1px no-repeat, 
    linear-gradient(blue, blue) left 0 bottom 30px/100% 1px no-repeat, 
    linear-gradient(blue, blue) top 0 left 20px/1px 100% no-repeat, 
    linear-gradient(blue, blue) top 0 right 60px/1px 100% no-repeat;
}

div.border:before {
  content: "";
  position: absolute;
  top: -10px;
  left: -10px;
  right: -10px;
  bottom: -10px;
  background: 
    linear-gradient(green, green) left 0 top 50px/100% 1px no-repeat, 
    linear-gradient(green, green) left 0 bottom 50px/100% 1px no-repeat, 
    linear-gradient(green, green) top 0 left 50px/1px 100% no-repeat, 
    linear-gradient(green, green) top 0 right 50px/1px 100% no-repeat;
}

div.box {
  background: repeating-linear-gradient(45deg, #000, #000 5px, transparent 5px, transparent 10px) border-box, red;
}

div.border {
  border-image: repeating-linear-gradient(45deg, #000, #000 5px, transparent 5px, transparent 10px);
  border-image-slice: 20 60 20 30;
  border-image-width: 50px;
  background: red;
}
<div class="box"></div>

<div class="border"></div>

现在更清楚了我们如何对图像进行切片,然后通过缩放拉伸将它们放在不同的区域中。同样清楚的是,最好的值是让所有边上的切片都等于 border-width,在您的示例中就是这种情况,因为 5em5x16px = 80px 因此 [=32 的切片=]


从规范中我们还可以读到:

The regions given by the border-image-slice values may overlap. However if the sum of the right and left widths is equal to or greater than the width of the image, the images for the top and bottom edge and the middle part are empty, which has the same effect as if a nonempty transparent image had been specified for those parts. Analogously for the top and bottom values.

如果您指定的左右切片大于图像宽度,那么从逻辑上讲,您将无法在 top/bottom/middle 部分放置任何内容:

div {
  width: 100px;
  height: 100px;
  border: solid 10px transparent;
  display: inline-block;
  position: relative;
}

div.box:before {
  content: "";
  position: absolute;
  top: -10px;
  left: -10px;
  right: -10px;
  bottom: -10px;
  background: 
    linear-gradient(blue, blue) left 0 top 20px/100% 1px no-repeat, 
    linear-gradient(blue, blue) left 0 bottom 30px/100% 1px no-repeat, 
    linear-gradient(blue, blue) top 0 left 60px/1px 100% no-repeat, 
    linear-gradient(blue, blue) top 0 right 60px/1px 100% no-repeat;
}

div.border:before {
  content: "";
  position: absolute;
  top: -10px;
  left: -10px;
  right: -10px;
  bottom: -10px;
  background: 
    linear-gradient(green, green) left 0 top 50px/100% 1px no-repeat, 
    linear-gradient(green, green) left 0 bottom 50px/100% 1px no-repeat, 
    linear-gradient(green, green) top 0 left 50px/1px 100% no-repeat, 
    linear-gradient(green, green) top 0 right 50px/1px 100% no-repeat;
}

div.box {
  background: repeating-linear-gradient(45deg, #000, #000 5px, transparent 5px, transparent 10px) border-box, red;
}

div.border {
  border-image: repeating-linear-gradient(45deg, #000, #000 5px, transparent 5px, transparent 10px);
  border-image-slice: 20 60 20 60;
  border-image-width: 50px;
  background: red;
}
<div class="box"></div>

<div class="border"></div>

同样的逻辑也适用于 top/bottom。

这里是一个例子,我们只有角:

div {
  width: 100px;
  height: 100px;
  border: solid 10px transparent;
  display: inline-block;
  position: relative;
}

div.box:before {
  content: "";
  position: absolute;
  top: -10px;
  left: -10px;
  right: -10px;
  bottom: -10px;
  background: 
    linear-gradient(blue, blue) left 0 top 20px/100% 1px no-repeat, 
    linear-gradient(blue, blue) left 0 bottom 100px/100% 1px no-repeat, 
    linear-gradient(blue, blue) top 0 left 60px/1px 100% no-repeat, 
    linear-gradient(blue, blue) top 0 right 60px/1px 100% no-repeat;
}

div.border:before {
  content: "";
  position: absolute;
  top: -10px;
  left: -10px;
  right: -10px;
  bottom: -10px;
  background: 
    linear-gradient(green, green) left 0 top 50px/100% 1px no-repeat, 
    linear-gradient(green, green) left 0 bottom 50px/100% 1px no-repeat, 
    linear-gradient(green, green) top 0 left 50px/1px 100% no-repeat, 
    linear-gradient(green, green) top 0 right 50px/1px 100% no-repeat;
}

div.box {
  background: repeating-linear-gradient(45deg, #000, #000 5px, transparent 5px, transparent 10px) border-box, red;
}

div.border {
  border-image: repeating-linear-gradient(45deg, #000, #000 5px, transparent 5px, transparent 10px);
  border-image-slice: 20 60 100 60;
  border-image-width: 50px;
  background: red;
}
<div class="box"></div>

<div class="border"></div>


使用百分比值也会得到相同的结果。我们只需要找到参考,因为我们正在处理渐变,所以渐变的大小就是元素的大小。在我们的示例中,50 的一部分等于 41.666%,因为 width/height 等于 100px 2 * 10px = 120px

div {
  width: 100px;
  height: 100px;
  border: solid 10px transparent;
  display: inline-block;
  position: relative;
}

div:before {
  content: "";
  position: absolute;
  top: -10px;
  left: -10px;
  right: -10px;
  bottom: -10px;
  background: 
    linear-gradient(blue, blue) left 0 top 50px/100% 1px no-repeat, 
    linear-gradient(blue, blue) left 0 bottom 50px/100% 1px no-repeat, 
    linear-gradient(blue, blue) top 0 left 50px/1px 100% no-repeat, 
    linear-gradient(blue, blue) top 0 right 50px/1px 100% no-repeat;
}


div.box {
  background: repeating-linear-gradient(45deg, #000, #000 5px, transparent 5px, transparent 10px) border-box, red;
}

div.border {
  border-image: repeating-linear-gradient(45deg, #000, #000 5px, transparent 5px, transparent 10px) 41.666%;
  border-image-width: 50px;
  background: red;
}
<div class="box"></div>

<div class="border"></div>