Треугольный вырез CSS с рамкой и прозрачным зазором

Мне нужно нарисовать следующий шаблон с помощью CSS в качестве разделителя между разделами моей страницы:

разделитель

Используя технику skewX() из этого ответа, я смог точно имитировать треугольный вырез (два псевдоэлемента добавлены к верхней части нижней части, один наклонен влево, а другой вправо, так что сквозь него виден фон верхней части):

введите здесь описание изображения

Но я не могу понять, как добавить границу, как показано на первом изображении.

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

Есть ли способ сделать это с помощью CSS?


person daGUY    schedule 21.04.2016    source источник


Ответы (3)


Это можно сделать с помощью CSS, но единственный подход, который я смог найти, — очень сложный, с использованием достаточного количества градиентов для имитации границ. Этого нельзя было сделать с обычными границами, потому что это приводило к тому, что либо границы выходили за пределы точки их пересечения, либо внутренняя область никогда не встречалась. Этого нельзя было сделать с тенями блока также потому, что зазор между границей и заполненной областью должен быть прозрачным. (Не говорю, что это невозможно сделать с ними, но просто я не смог заставить это работать, может быть, и с ними тоже есть способ).

Ниже показано, как мне удалось достичь границы:

  • Для создания необходимых границ использовались четыре линейных градиента.
  • 1-й и 2-й на самом деле являются сплошными цветами (то есть нет перехода от одного цвета к другому), но я использовал градиенты, потому что это позволяет нам контролировать размер фона. Эти два создают сплошную горизонтальную линию, где один расположен слева, а другой расположен справа немного выше прозрачного разреза, созданного с использованием псевдоэлементов.
  • 3-я и 4-я производят угловые линии той толщины, которая требуется для границы.
  • Размер и положение всех четырех градиентов установлены таким образом, чтобы они создавали эффект границы.

Вывод — как вы можете видеть, наведя курсор на элемент — тоже отзывчивый.

div {
  position: relative;
  height: 200px;
  width: 300px;
  background: linear-gradient(#DDD, #DDD), /* horizontal border line on left */
              linear-gradient(#DDD, #DDD),  /* horizontal border line on right */
              linear-gradient(to top right, transparent calc(50% - 2px), #DDD calc(50% - 2px), #DDD calc(50% + 2px), transparent calc(50% + 2px)), /* angled border on left of center */
              linear-gradient(to top left, transparent calc(50% - 2px), #DDD calc(50% - 2px), #DDD calc(50% + 2px), transparent calc(50% + 2px)), /* angled border on right of center */
              radial-gradient(circle, #3F9CBA 0%, #153346 100%) /* actual background */;
  background-size: calc(50% - 38px) 4px, 
                   calc(50% - 38px) 4px, 
                   40px 40px, /* size of one half of the triangle */
                   40px 40px, /* size of one half of the triangle */
                   auto auto /* size of actual bg */;
  background-position: 0% calc(100% - 44px), 
                       100% calc(100% - 44px), 
                       calc(50% - 18px) calc(100% - 8px), 
                       calc(50% + 18px) calc(100% - 8px), 
                       0px 0px;
  background-repeat: no-repeat;
  overflow: hidden;
  border-bottom: 20px solid #DDD;
}
div:before,
div:after {
  position: absolute;
  content: '';
  height: 40px;
  width: 50%;
  bottom: 0;
  background: #DDD;
  backface-visibility: hidden;
}
div:before {
  left: 0;
  transform-origin: left bottom;
  transform: skewX(45deg);
}
div:after {
  right: 0;
  transform-origin: right bottom;
  transform: skewX(-45deg);
}

/* Just for demo */

div {
  transition: all 1s;
}
div:hover {
  height: 250px;
  width: 550px;
}
<div></div>


Ниже показано, как должны быть установлены свойства фона на основе требуемого цвета и ширины границы (все расчеты для угла наклона 45 градусов, для разных углов потребуются другие расчеты):

  • Цвета, используемые в градиенте, совпадают с цветом границы. Таким образом, если цвет границы должен быть красным вместо #DDD, тогда все значения цвета должны быть изменены на красный.
  • Толщина границы будет определяться с помощью background-size для первых двух градиентов и с использованием точек остановки цвета градиента для следующих двух градиентов.

    • For the first two, the background-size in Y-axis is nothing but the border thickness. Change it to suit the needed thickness. The background-size in X-axis is nothing but 50% minus the height of the pseudo minus half of border thickness
    • Для следующих двух точек остановки цвета градиента следует установить так, чтобы цвет начинался (и заканчивался прозрачным) на 50% - половине толщины границы и заканчивался цветом (начало прозрачности) и 50% + половина толщины границы (т.е. что он производит штрих необходимой толщины).
  • The background-position should be set such that the required gap is there between the colored area and the border.
    • For the first two, the background-position in X-axis should be left edge (0%) and right edge (100%) respectively. Their position in Y-axis should be above the pseudo-element and so it should be 100% minus pseudo-element's height minus the spacing.
    • Для следующих двух, background-position включает в себя более сложные вычисления, включающие расстояние между границами, толщину и высоту псевдоэлемента. Логика присутствует ниже.
background: linear-gradient([color, [color]),
            linear-gradient([color], [color]), 
            linear-gradient(to top right, 
                            transparent calc(50% - [border-thickness/2]), 
                            [color] calc(50% - [border-thickness/2]), 
                            [color] calc(50% + [border-thickness/2]),
                            transparent calc(50% + [border-thickness/2])),
            linear-gradient(to top left, 
                            transparent calc(50% - [border-thickness/2]),
                            [color] calc(50% - [border-thickness/2]), 
                            [color] calc(50% + [border-thickness/2]), 
                            transparent calc(50% + [border-thickness/2])), 
                            radial-gradient(circle, #3F9CBA 0%, #153346 100%) /* actual background */;

background-size: calc(50% - [pseudo-height - border-thickness/2]) [border-thickness], 
                 calc(50% - [pseudo-height - border-thickness/2]) [border-thickness], 
                 [pseudo-height] [pseudo-height], 
                 [pseudo-height] [pseudo-height], 
                 auto auto /* size of actual bg */;

background-position: 0% calc(100% - [pseudo-height + border-space]), 
                     100% calc(100% - [pseudo-height + border-space]), 
                     calc(50% - [(pseudo-height - border-space)/2]) calc(100% - [border-space + border-thickness/2]), 
                     calc(50% + [(pseudo-height - border-space)/2]) calc(100% - [border-space + border-thickness/2]), 
                     0px 0px;

Если вам нужен прозрачный вырез с рамкой в ​​верхней части нижнего div, вы можете сделать это, как показано в приведенном ниже фрагменте.

div {
  height: 200px;
  width: 300px;
}
div:nth-child(1) {
  background: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
  background-repeat: no-repeat;
}
div:nth-child(2) {
  position: relative;
  margin-top: -48px;
  padding-top: 48px;
  background: linear-gradient(#DDD, #DDD), linear-gradient(#DDD, #DDD), linear-gradient(to top right, transparent calc(50% - 2px), #DDD calc(50% - 2px), #DDD calc(50% + 2px), transparent calc(50% + 2px)), linear-gradient(to top left, transparent calc(50% - 2px), #DDD calc(50% - 2px), #DDD calc(50% + 2px), transparent calc(50% + 2px)), linear-gradient(#DDD, #DDD);
  background-size: calc(50% - 38px) 4px, calc(50% - 38px) 4px, 40px 40px, 40px 40px, auto auto;
  background-position: 0% 0px, 100% 0px, calc(50% - 18px) 0px, calc(50% + 18px) 0px, 0px 0px;
  background-repeat: no-repeat;
  background-clip: border-box, border-box, border-box, border-box, content-box;
  overflow: hidden;
}
div:nth-child(2):before,
div:nth-child(2):after {
  position: absolute;
  content: '';
  height: 40px;
  width: 50%;
  top: 8px;
  background: #DDD;
  backface-visibility: hidden;
}
div:before {
  left: 0;
  transform-origin: left bottom;
  transform: skewX(45deg);
}
div:after {
  right: 0;
  transform-origin: right bottom;
  transform: skewX(-45deg);
}
/* Just for demo */

div {
  transition: all 1s;
}
body:hover > div {
  height: 250px;
  width: 550px;
}
<div></div>
<div></div>

person Harry    schedule 22.04.2016
comment
Хороший ответ, как всегда! Но я думаю, что ОП хочет, чтобы этот результат был на нижнем элементе, а не на верхнем (не уверен в этом, но так я это прочитал) - person vals; 22.04.2016
comment
Спасибо @vals. Я перечитал вопрос сейчас, и вы делаете правильный вывод. Тем не менее, можно использовать тот же подход. Просто нужно расположить градиенты сверху, задать отступ элемента, равный высоте псевдо + граница + пробел, и обрезать фон элемента. Завтра утром добавлю образец :) - person Harry; 22.04.2016
comment
Да, ты прав. Но я все равно не мог не опубликовать еще один ответ :-) - person vals; 22.04.2016

здесь другой подход с градиентом и (@Harry) границами и тенью: очень помогает известная ширина, иначе вам нужно настроить некоторые значения смещения теней с помощью calc().

div.bdr {
  padding-bottom: 5px;/* to be used to draw a transparent line */
  background: #1B4046;
  background-clip: content-box;/* here comes the 5 transparent pixel line */
  box-shadow: -273px 5px 0 #1B4046, 274px 5px 0 #1B4046; 
  /* move shadow far away to only draw them by bits 
  Notice, wrapper must be hidding overflow */
  margin-bottom:2em;
}

.bdr.bg-color{
  background-color:tomato;
  background-image:linear-gradient(to top, transparent 5px,   #1B4046 5px);

  background-clip:border-box;
  }
div.bdr:after {  
  content: '';
  height: 30px;
  width: 30px;
  padding: 5px; 
  display: block;
  margin: auto;
  z-index: -1;
  transform: rotate(45deg);
  margin: -20px auto -20px;
  background:  #1B4046;
  background-clip: content-box;/* again, keep some parts transparent */
  box-shadow:6px 6px 0  -1px #1B4046;  
}
.bdr.bg-color:after {
  z-index:0;
  padding:5px 0 0 5px;
  border:5px solid transparent;
  border-left:0;
  border-top:0;
  box-shadow:6px 6px 0  -1px #1B4046;
  
  background:linear-gradient(to bottom right, transparent 50%, #1B4046 50%) no-repeat , linear-gradient(to bottom right, transparent 50%, tomato 50%) 5px 5px  no-repeat;
  }
article {
  width:500px;
  margin:auto;
  color:white;
}
h1 {
  text-align:center;
}
div.shapebdr {
  padding-bottom: 40px;
  margin: 32px 0;
  background: linear-gradient(to top, transparent 30px, #1B4046 30px, #1B4046 35px, transparent 35px) bottom left, linear-gradient(to top, transparent 30px, #1B4046 30px, #1B4046 35px, transparent 35px) bottom right, linear-gradient(to top, transparent 40px, #1B4046 40px) bottom;
  ;
  background-repeat: no-repeat;
  background-size: 230px 100%, 230px 100%, 100% 100%;
  position: relative;
}

div.shapebdr:after {
  content: '';
  height: 40px;
  width: 40px;
  padding: 10px;
  display: block;
  margin: auto;
  z-index: -1;
  background: inherit;
  transform: rotate(45deg);
  margin: -40px auto -20px;
  background: linear-gradient(to top, #1B4046, #1B4046) no-repeat, linear-gradient(to top, #1B4046 0, #1B4046 5px, transparent 5px) no-repeat right, linear-gradient(to left, #1B4046 0, #1B4046 5px, transparent 5px) no-repeat bottom right;
  background-clip: content-box, border-box, border-box;
  background-size: auto auto, 57% 100%, 100% 57%;
}

div.br:after {
  margin: auto;
  margin-bottom: -40px;  
}


article {
  overflow: hidden;
  padding-bottom: 40px;
}
html {
  min-height: 100%;
  background: linear-gradient(30deg, gray, yellow, purple, lime, tomato, turquoise, gray);
}
p {
  padding:0.25em;
  margin:0.25em;
position:relative;/* make sure text shows over the pseudo element */
}
<article>
  <h1>border and shadow approach </h1>
  <div class="bdr">
    <p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est.
      facilisis luctus, metus</p>
  </div>
  <h1>gradient approach</h1>
  <div class="shapebdr">
    <p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est.
      lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis. Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor,
      facilisis luctus, metus</p>

  </div>
   <h1>shadow + gradient approach to fill translucide line</h1> 
  <div class="bdr bg-color">
    <p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est.
      lacus enim ac dui. Donec non enim in turpis pulvinar facilisis. Ut felis. Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor,
      facilisis luctus, metus</p>

  </div>
</article>

ручка для игры

person G-Cyrillus    schedule 22.04.2016
comment
Хорошо :) Только небольшая проблема, я вижу небольшой треугольник, перекрывающий содержимое в подходе границы и тени. - person Harry; 22.04.2016
comment
Хороший ответ тоже, но я понял вопрос как раз наоборот (фон базы показан в верхней части) - person vals; 22.04.2016
comment
@ Гарри, я этого не вижу, не могли бы вы предоставить снимок экрана и сказать мне, какой браузер / ОС работает. - person G-Cyrillus; 23.04.2016
comment
@vals я не понимаю (вероятно, мой английский слишком плохой), моя демонстрация слишком показывала полупрозрачный фрагмент, но его можно заполнить любым фоновым цветом, с моего компьютера градиент и тень одинаковы (w7/последний FF/Chrome/IE /... ) - person G-Cyrillus; 23.04.2016
comment
@GCyrillus: Это то, что я вижу в последней версии Chrome (сборка dev-m) + Выигрыш 10. - person Harry; 23.04.2016
comment
В этом codepen codepen.io/anon/pen/QNBBNe я изменил повернутое после на сделайте его более заметным... просто измените второй цвет на прозрачный, чтобы все было в порядке - person vals; 23.04.2016
comment
Извините, мой английский тоже плохой. Из вопроса я так понял, что граница установлена ​​на нижнем элементе, (тот, что в нижней части экрана). Во всяком случае, может ошибаться. - person vals; 23.04.2016
comment
@harry Я вижу, z-индекс был настроен на 0 вместо -1, .position+ z-index на p тоже может это вылечить. - person G-Cyrillus; 23.04.2016
comment
@vals Я вижу, z-индекс был настроен на 0 вместо -1, .position+ z-index на p тоже может это вылечить. ваш исправленный с помощью режима смешивания смешивания показывает интересную дыру в любом случае - person G-Cyrillus; 23.04.2016

Не удержался и выложил еще одну версию...

Слишком много рассчитанных вручную значений, чтобы сделать это идеальным решением, но может подойти вам, если размер фиксирован.

.test {
  width: 400px;
  height: 80px;
  margin-top: 100px;
  position: relative;
  overflow: hidden;
  background-image: linear-gradient(-120deg, transparent 31px, lightblue 21px),                       linear-gradient(120deg, transparent 31px, lightblue 21px);
  background-size: 50% 80%, 50% 80%;
  background-repeat: no-repeat;
  background-position: 0px 100%, 100% 100%;
}


.test:before {
  content: "";
  position: absolute;
  width: 50%;
  height: 50px;
  border-top: solid 4px blue;
  border-right: solid 4px blue;
  top: 0px;
  right: calc(50% - 2px);
  transform: skewX(30deg);
  transform-origin: right bottom;
}

.test:after {
  content: "";
  position: absolute;
  width: 50%;
  height: 50px;
  border-top: solid 4px blue;
  border-left: solid 4px blue;
  top: 0px;
  left: calc(50% - 2px);
  transform: skewX(-30deg);
  transform-origin: right bottom;
}
<div class="test"></div>

person vals    schedule 22.04.2016