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

7
我正在尝试理解边框图像渐变中的border-image-slice是如何工作的。在规范中,border-image-slice的值可以是数字,它表示栅格图像的边缘偏移量和矢量图像的坐标。对于矢量图像,该数字相对于元素的大小而非源图像的大小,因此在这些情况下通常更喜欢使用百分比。在CSS-tricks的文章示例中设置了一个border-image,如下所示:
border-image: repeating-linear-gradient(45deg, 
        #000, #000 1.5%, 
        transparent 1.5%, transparent 5%) 80;

所以,根据规范,80是相对于div的大小(宽度:26em;高度:23em)的。但我仍然不理解它的含义。当我改变div的宽度或高度时,边框图像并没有改变其外观。但是当我改变border-image-slice或边框宽度时,外观发生了显著变化。因此,似乎数字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>


简而言之,为了获得最佳渲染效果,切片应该等于边框宽度。在您的情况下,5em = 80px,因此我们使用80。我们将尝试提供详细的答案。 - Temani Afif
2个回答

9

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并指定了一个与边框宽度不同的边框图像宽度,并使用了与该边框宽度相等的切片。
请注意,在切片中的50在这里被视为像素,因为我们正在处理非矢量图像(渐变)。
引用:
数字代表图像中的像素(如果图像是光栅图像)或向量坐标(如果图像是矢量图像)。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>

如果存在fill关键字,则导致border-image的中间部分被保留。(默认情况下,它会被丢弃,即视为空。)ref

默认情况下,边框图像不会在中间绘制,而只会在边框上绘制。从示例中我们可以清楚地看到,每个侧面都有50px,我们的自定义边框也由border-image-width定义。

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

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

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

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

这个属性指定了图像顶部、右侧、底部和左侧边缘的内部偏移量,将其分为九个区域:四个角落四条边缘和一个中心

enter image description here 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>

左侧图像是我们分成九个部分的原始图像,然后我们将每个部分放置在右侧九个区域中。中间的那个是空的,因为我们没有使用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 个部分,其中边框宽度仍为相同的 (50px)。您可以清晰地看到角落中的部分是如何简单缩放和边缘变形的。
在每个角落中,我们使用一个 25px 25px 的图像放在一个 50px 50px 的区域内,例如在顶部边缘中,我们使用一个 60px 25px 的图像放在一个 10px 50px 的区域内。
您还可以定义每个侧面的不同值,以获得以下效果:

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,这在你的示例中是成立的,因为5em等于5x16px = 80px,因此切片大小为80
从规范中我们还可以读到:
边框图像切片值给出的区域可能会重叠。但是,如果右侧和左侧宽度之和等于或大于图像的宽度,则顶部和底部边缘以及中间部分的图像为空,这与为这些部分指定非空透明图像具有相同的效果。对于上下值也是类似的。
如果您指定的左右切片大于图像宽度,那么逻辑上您将无法放置在顶部/底部/中间部分的任何内容:

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>

同样的逻辑也适用于顶部/底部。
以下是一个例子,我们只会有角落:

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%,因为宽度/高度等于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>


非常感谢您提供如此详细的答案。我错误地认为渐变是矢量图像。如果我们有一个矢量图像而不是渐变,您能告诉我数字80将处于哪个单位(因为它应该是坐标)吗? - Elistan
@Elistan 好的,我会尝试用矢量图像给你举例子 ;) - Temani Afif

4
在下一个例子中,我使用px而不是em,因为我认为这更清晰。
这是用于边框图像的图片。

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

这个图片将被分割成9个小正方形,就像一个网格一样。

enter image description here

这张图片来源于这篇文章: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-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>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接