Как изображение границы работает с линейным градиентом?

#html #css #twitter-bootstrap #sass

#css #граница #линейные градиенты

Вопрос:

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

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

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

 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,5 эм, 16 для 1 эм и т.д.).

Мой вопрос в том, как вычисляется число 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> 

Комментарии:

1. TL; DR фрагмент должен быть равен ширине границы для наилучшего отображения. В вашем случае 5em = 80 пикселей, поэтому мы используем 80 .. постараемся дать подробный ответ

Ответ №1:

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> 

В приведенном выше я попытался создать два divs с одинаковым выводом, используя разные методы (background и border). Обратите внимание, как во втором примере я использую ключевое fill слово, и я указал border-image-width значение, отличное от ширины границы, и использовал фрагмент, равный этой ширине границы.

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

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


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

Области, заданные значениями 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> 

Та же логика применима и к 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% , поскольку ширина / высота равна 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> 

Комментарии:

1. большое спасибо за такой подробный ответ. Я ошибочно предположил, что градиент — это векторное изображение. Не могли бы вы сказать, пожалуйста, если бы вместо градиента у нас было векторное изображение, число 80 было бы в каких единицах (поскольку это должны быть координаты)?

2. @Elistan ok попытается привести вам пример с векторным изображением 😉

Ответ №2:

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