Преобразование пути клипа CSS со смешанными (фиксированными и процентными) значениями в путь клипа SVG

У меня есть изображение карты с градиентом с обрезанным углом, используя clip-path:

.card {
  width: 200px;
  height: 200px;
  background: linear-gradient(to bottom, blue, green);
  clip-path: polygon(20px 0, 100% 0, 100% 100%, 0 100%, 0 20px);
}
<div class="card"></div>

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

Но у clip-path на данный момент не самая лучшая поддержка браузеров, поэтому я попытался преобразовать этот HTML и CSS в SVG.

.container {
  width: 200px;
  height: 200px;
}
<div class="container">
  <svg viewBox="0 0 100 100" clip-path="url(#myClip)">
    <defs>
      <linearGradient id="grad1" x1="0%" y1="0%" x2="0%" y2="100%">
        <stop offset="0%" style="stop-color:rgb(0,0,255);stop-opacity:1" />
        <stop offset="100%" style="stop-color:rgb(0,255,0);stop-opacity:1" />
      </linearGradient>
    </defs>
  
    <polygon points="20,0 100,0 100,100 0,100 0,20" fill="url(#grad1)" />
 </svg>
</div>

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


person Vadim Ovchinnikov    schedule 12.12.2018    source источник
comment
градиент будет случайным или заданным?   -  person Temani Afif    schedule 12.12.2018
comment
@TemaniAfif Было бы здорово, если бы я получил решение, которое может работать с произвольным градиентом или изображением. Даже если это не решение SVG, но оно будет работать в любом современном браузере.   -  person Vadim Ovchinnikov    schedule 12.12.2018
comment
добавил универсальное решение   -  person Temani Afif    schedule 12.12.2018


Ответы (3)


Чтобы сохранить фиксированный размер, вы не можете использовать viewBox в своем SVG. Просто обрежьте нужный угол, а другие углы вытяните далеко, чтобы они покрывали любой размер, который вам может понадобиться. В приведенном ниже примере я расширил clippath до (10000,10000).

Здесь мы применяем градиент к 100% x 100% <rect>. Это делается для того, чтобы градиент всегда масштабировался в соответствии с размером экрана. Затем мы применяем clippath к прямоугольнику, чтобы получить выемку.

html, body {
  height: 100%;
}

.container {
  width: 50%;
  height: 50%;
}
<div class="container">
  <svg width="100%" height="100%">
    <defs>
      <linearGradient id="grad1" x1="0%" y1="0%" x2="0%" y2="100%">
        <stop offset="0%" style="stop-color:rgb(0,0,255);stop-opacity:1" />
        <stop offset="100%" style="stop-color:rgb(0,255,0);stop-opacity:1" />
      </linearGradient>
      <clipPath id="clip1">
        <polygon points="20,0 10000,0 10000,10000 0,10000 0,20"/>
      </clipPath>
    </defs>
  
    <rect width="100%" height="100%" fill="url(#grad1)" clip-path="url(#clip1)"/>
 </svg>
</div>

person Paul LeBeau    schedule 13.12.2018
comment
Я нашел решение с использованием SVG mask, но у вас есть мой голос, потому что ваше решение также работает, хотя и выглядит хакерским с этим значением 10000. Так что все равно спасибо! - person Vadim Ovchinnikov; 13.12.2018
comment
@Paul LeBeau Очень остроумный ответ! Работает во всех современных браузерах + Edge - person Alexandr_TT; 14.12.2018

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

.card {
  width: 200px;
  height: 200px;
  padding-top: 20px;
  background-image: linear-gradient(to bottom, blue,red,yellow,green); 
  background-clip:content-box;
  background-size:100% 200px; /*same as height*/
  position: relative;
  z-index:0;
  overflow:hidden;
  box-sizing: border-box;
  display:inline-block;
}

.card:before {
  content: "";
  position: absolute;
  z-index:-1;
  top: 0;
  padding: inherit;
  left: 0;
  right: 0;
  background-image: inherit;
  background-size:inherit;
  transform: skewX(-45deg);
  transform-origin: left bottom;
}

body {
  background:pink;
}
<div class="card"></div>
<div class="card" style="background-image:linear-gradient(to top,white,purple,green ,red 90%, blue"></div>

Для любого градиента или любого изображения вы можете добавить дополнительный элемент, чтобы исправить перекос:

.card {
  width: 200px;
  height: 200px;
  padding-top: 20px;
  background-image: linear-gradient(to bottom, blue,red,yellow,green); 
  background-clip:content-box;
  background-size:auto 200px; /*same as height*/
  position: relative;
  z-index:0;
  overflow:hidden;
  box-sizing: border-box;
  display:inline-block;
}
.image {
  background-size:cover; /*less restriction when it comes to image*/
}


.card span,
.card span::before {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  background-image: inherit;
  background-size:inherit;
  transform-origin: left bottom;
}

.card span {
  z-index:-1;
  padding: inherit;
  transform: skewX(-45deg);
  overflow:hidden;
}
.card span:before {
   content:"";
   bottom:0;
   transform: skewX(45deg);
}

body {
  background:pink;
}
<div class="card">
<span></span>
</div>
<div class="card" style="background-image:linear-gradient(60deg,white,purple,green ,red 90%, blue)">
<span></span>
</div>

<div class="card image" style="background-image:url(https://picsum.photos/400/400?image=0)">
<span></span>
</div>

<div class="card image" style="background-image:url(https://picsum.photos/600/600?image=15)">
<span></span>
</div>

person Temani Afif    schedule 12.12.2018
comment
Если вам нужен мой отзыв, то я могу сказать, что мне нравится, что он отлично работает, так что у вас есть мой голос, но мне не нравится, что для этого нужно так много кода, поэтому я подожду, если появятся другие варианты. - person Vadim Ovchinnikov; 12.12.2018
comment
@VadimOvchinnikov да, конечно, вам нужно подождать, потому что я почти уверен, что с SVG есть способ. Просто ждите волшебников ;) но мне нравится хакинг с помощью CSS :p - person Temani Afif; 12.12.2018

С помощью Stack Overflow на русском с использованием SVG mask мое решение таково

.container {
  width: 200px;
  height: 200px;
}

svg {
  width: 100%;
  height: 100%;
}
<div class="container">
  <svg>
    <defs>
      <mask id="triangle-clip">
        <rect x="0" y="0" width="100%" height="100%" fill="#fff" />
        <path d="M0,20 v-20 h20 z" fill="#000" />
      </mask>

    <linearGradient id="grad1" x1="0%" y1="0%" x2="0%" y2="100%">
      <stop offset="0%" style="stop-color:rgb(0,0,255);stop-opacity:1" />
      <stop offset="100%" style="stop-color:rgb(0,255,0);stop-opacity:1" />
    </linearGradient>
  </defs>
  <rect width="100%" height="100%" fill="url(#grad1)" mask="url(#triangle-clip)" />
</svg>
</div>

person Vadim Ovchinnikov    schedule 13.12.2018