NoneBorder -image-slice для градиентного изображения границы

Я пытаюсь понять, как работает border-image-slice в случае градиентного изображения границы. В спецификации написано, что значение для border-image-slice может быть числом, которое

Представляет смещение края в пикселях для растровых изображений и координаты для векторных изображений. Для векторных изображений число относится к размеру элемента, а не к размеру исходного изображения, поэтому в этих случаях обычно предпочтительны проценты.

В примерах из статьи CSS-трюки изображение границы устанавливается следующим образом:

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>

2 ответа

Решение

TL;DR

При использовании градиента размер изображения равен размеру элемента. border-width-image определит 9 областей, где мы будем размещать срезы (если не определены, border-width используется). border-image-slice рассмотрим исходное изображение для создания срезов. Значение без единиц измерения рассматривается как значение пикселя, а процентное значение сопоставляется с размером элемента.

Чтобы получить идеальный результат, мы должны иметь срезы, равные регионам, и для этого нам нужно иметь border-slice-image равно border-width-image (или же 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-width-image отличается от ширины границы и используется срез, равный этой ширине границы.

Обратите внимание, что 50 здесь срез рассматривается как пиксель, так как мы имеем дело с не векторным изображением (градиент).

Числа представляют пиксели в изображении (если изображение является растровым изображением) или векторные координаты (если изображение является векторным изображением). ссылка

Давайте удалим свойство 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, если оно присутствует, обеспечивает сохранение средней части изображения границы. (По умолчанию он отбрасывается, т. Е. Обрабатывается как пустой.) Ref

По умолчанию изображение границы отображается не по центру, а только по границе. Мы можем ясно видеть пример, который мы имеем 50px на каждой стороне с нашей пользовательской границей, как определено border-image-width

И если мы не укажем border-image-width значение по умолчанию 1 что значит

Числа представляют собой кратные соответствующих вычисленных border-width,

Так что либо мы явно указываем border-image-width или мы просто используем border-width в качестве ссылки. Только в большинстве случаев border-width необходимо, так как в большинстве случаев мы хотим охватить только пограничную область, а не более.

Теперь срез разделит изображение на 9 частей

Это свойство определяет внутренние смещения от верхнего, правого, нижнего и левого краев изображения, разделяя его на девять областей: четыре угла, четыре края и середина

ссылка

Вот шаги, которые лучше покажут, как это делается для нашего примера:

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 областей правого. Средний пустой, потому что мы не использовали заливку. В этом примере мы ничего не заметим, потому что срезы соответствуют регионам.

Теперь давайте уменьшим ломтик до 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>

Теперь стало понятнее, как мы нарезаем изображение, а затем помещаем их в другую область, масштабируя их растягивая. Также ясно, что наилучшим значением является наличие срезов во всех сторонах, равных ширине границы, что имеет место в вашем примере, так как 5em является 5x16px = 80px таким образом, кусок 80


Из спецификации мы также можем прочитать:

Области, заданные значениями border-image-slice, могут перекрываться. Однако если сумма правой и левой ширины равна или больше ширины изображения, изображения для верхнего и нижнего края и средней части будут пустыми, что будет иметь тот же эффект, как если бы непустое прозрачное изображение было указано для тех частей. Аналогично для верхних и нижних значений.

Если вы укажете левый и правый срез больше ширины изображения, то по логике вы не получите ничего для верхней / нижней / средней части:

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>

В следующем примере я использую 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 используются для рисования краев.

Если вместо 80 вы используете 208 или 50%, изображение границы получит углы, но не края, потому что для краев ничего не осталось.

Далее идет демонстрация, где вы можете увидеть эволюцию срезов на изображении, используемом для рисования границы изображения. Я изменил ширину 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>

Другие вопросы по тегам