border-image-slice для градиентного изображения границы - PullRequest
3 голосов
/ 06 июля 2019

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

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

В примерах из CSS-трюков article 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>

Ответы [ 2 ]

1 голос
/ 06 июля 2019

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 в срезездесь рассматривается как пиксель, поскольку мы имеем дело с не векторным изображением (градиент).

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

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

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

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

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

Теперь фрагмент будет разделять изображение на 9 частей

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

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>

Левое изображение - это оригинальное изображение, которое мы делим на 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>
1 голос
/ 06 июля 2019

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

Если вместо 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>
...