Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Complex clip-path / SVG background of an element

I need to create something like the image below:

enter image description here

Where:

  • The black background represents a element on the page, header for example
  • The cyan background represents the overall background underneath the black one
  • the black background element has to be treated like a single element, because it will have a pattern overlay on top of it, that will have to stay within the borders of the black element, and not show up outside of it

It would be very easy, to just create the black element with some :pseudo-elements, however that pattern on top of it has brought the whole thing to a standstill.

I've been reading about clip-path prop. but I'm not sure I would be able to create a complex clip like this one (or maybe it seems complex to me).

The whole thing will be used on a iOS app, and so far it seems this clip-path property would be compatible with it. Another thing to mention, the black element will have a fixed height, but has to be 100% width of its parent. I've figured I'd get away with using svg instead, but as it needs to be a fixed height, it seems like its distorting when its stretched.


UPDATE: The right side has to stay the same width, I've figured maybe using a two svgs inside a <g> tag and absolute position them, one would be fluid and the other would have a fixed width. However, I'm not sure if a filter would cover both of them, or if the filter can be applied at all to a <g> tag, inside a svg

SVG sample below:

body {
  background: cyan;
}
svg {
  min-width: 100%;
  height: 80px;
}
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 452 170" preserveAspectRatio="none">
  <rect x="1" y="14" width="438" height="142" />
  <path d="M0 0v170h452V0H0zM448 166H4V4h444V166z" />
  <rect y="14" width="438" height="142" />
</svg>

Any tips or pointers are much appreciated!

like image 999
RGLSV Avatar asked Dec 26 '15 00:12

RGLSV


People also ask

Can an SVG have a background image?

SVG images can be used as background-image in CSS as well, just like PNG, JPG, or GIF. All the same awesomeness of SVG comes along for the ride, like flexibility while retaining sharpness. Plus you can do anything a raster graphic can do, like repeat.

How do I use clip-path in SVG?

The clip-path property is used to specify a clipping path that is to be applied to an element. Using clip-path , you can apply an SVG <clipPath> to an element by referencing that path in the property value. You can also define a clipping path using one of the basic shapes defined in the CSS Shapes module.

How do you scale a clip-path?

Scaling the clip-path The simplest way to do this is to go back to our image editing software and resize our SVG to have a maximum width/height of 1px . Here's the same SVG we saw earlier, resized. The 1px dot may be difficult to see but, most importantly, the values are all between 0 and 1 .


2 Answers

May be it would be a better solution go with masking ?

#test {
  font-size: 100px;
  position: relative;
  display: inline-block;
  margin: 40px;
}

#test:after {
  content: "";
  position: absolute;
  left: 0px;
  right: 0px;
  top: 0px;
  bottom: 0px;
  z-index: -1;
  background: repeating-linear-gradient(45deg, lightblue, tomato 100px);
  -webkit-mask-image: linear-gradient(red, red),
                    linear-gradient(red, red), 
                    linear-gradient(red, red), 
                    linear-gradient(red, red), 
                    linear-gradient(red, red), 
                    linear-gradient(transparent, transparent);
  -webkit-mask-size: 5% 100%, 5% 100%, 100% 5%, 100% 5%, 80% 80%;
  -webkit-mask-position: left top, right top, center top, center bottom, center center ;
  -webkit-mask-repeat: no-repeat;
}

body {
  background-color: lightgreen;
}
<div id="test">Transparent frame</div>

A fixed width approach

replace the dimensions of the width of the borders with a fixed value in pixels. Use calc for the inner rectangle.

body, html {
    width: 90%;
  position: relative;
}

#test {
  font-size: 100px;
  position: relative;
  display: inline-block;
  margin: 40px;
  width: 100%;
  height: 40%;
}

#test:after {
  content: "";
  position: absolute;
  left: 0px;
  right: 0px;
  top: 0px;
  bottom: 0px;
  z-index: -1;
  background: repeating-linear-gradient(45deg, lightblue, tomato 100px);
  -webkit-mask-image: linear-gradient(red, red),
                    linear-gradient(red, red), 
                    linear-gradient(red, red), 
                    linear-gradient(red, red), 
                    linear-gradient(red, red), 
                    linear-gradient(transparent, transparent);
  -webkit-mask-size: 10px 100%, 10px 100%, 100% 10px, 100% 10px, calc(100% - 40px) calc(100% - 40px);
  -webkit-mask-position: left top, right top, center top, center bottom, center center ;
  -webkit-mask-repeat: no-repeat;
}

body {
  background-color: lightgreen;
}
<div id="test">Transparent frame</div>

An eliptical example

#test {
  font-size: 100px;
  position: relative;
  display: inline-block;
  margin: 40px;
  border-radius: 50%;
}

#test:after {
  content: "";
  position: absolute;
  left: 0px;
  right: 0px;
  top: 0px;
  bottom: 0px;
  z-index: -1;
  border-radius: 50%;
  background: repeating-linear-gradient(45deg, lightblue, tomato 100px);
  -webkit-mask-image: radial-gradient(ellipse, red 55%, transparent 56%, transparent 65%, red 66%);
}

body {
  background: lightgreen;
}
<div id="test">Transparent frame</div>
like image 153
vals Avatar answered Oct 13 '22 23:10

vals


This can be achieved by making use of the approach described by Ana in this CSS Tricks article.

Using CSS Clip-path:

All we need to do is use a polygon for clipping like in the below snippet and apply it on the container. Such a path would show the background image in the gap between the outermost box and the middle box, hide or clip the background image between the middle box and the innermost box.

div {
  height: 200px;
  width: 100%;
  background: url(http://lorempixel.com/800/200/abstract/6);
}
#css-pattern {
  -webkit-clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%, 0px 0px, 20px 20px, 20px calc(100% - 20px), calc(100% - 20px) calc(100% - 20px), calc(100% - 20px) 20px, 20px 20px, 40px 40px, 40px calc(100% - 40px), calc(100% - 40px) calc(100% - 40px), calc(100% - 40px) 40px, 40px 40px);
  clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%, 0px 0px, 20px 20px, 20px calc(100% - 20px), calc(100% - 20px) calc(100% - 20px), calc(100% - 20px) 20px, 20px 20px, 40px 40px, 40px calc(100% - 40px), calc(100% - 40px) calc(100% - 40px), calc(100% - 40px) 40px, 40px 40px);
}

/* just for demo */

body {
  background: radial-gradient(circle at center, aliceblue, mediumslateblue);
  min-height: 100vh;
}
<h3>Pure CSS Clip-path with fixed width gap on all 4 sides</h3>
<div id='css-pattern'></div>

Using SVG Clip-path:

SVG clip-path offers better browser support than the CSS version as it is supported by Firefox too. All we need to do is create a path like in the below snippet and use it for clipping the container.

svg path {
  fill: transparent;
  stroke: black;
}

/* Just for fun */

path {
  animation: draw 5s linear;
  stroke-dasharray: 4450;
}
@keyframes draw {
  from {
    stroke-dashoffset: -4450;
  }
  to {
    stroke-dashoffset: 0;
  }
}
<svg width='600px' height='200px'>
  <path d='M0,0 600,0 600,200 0,200 0,0 20,20 20,180 580,180 580,20 20,20 40,40 40,160 560,160 560,40 40,40 z' />
</svg>

SVG implementation would have been far easier if the dimensions of your container were static. Since they are not static, fraction values cannot be used as the value would differ based on the actual width of the element (whatever 100% corresponds to). Say for example, a fraction value of 0.2 would mean 40px for a 200px wide element and would mean 80px for a 400px wide element. You can see how this affects the output in the snippet's first sample.

One way to overcome this would be to make use of JavaScript (or any other scripting library that you prefer), get the actual calculated width of the element in pixels and the calculate the coordinate values of the path's d attribute based on it. The second sample in the below snippet uses this method.

Note: Clip-path is not supported by IE but since you are creating an iOS app, I think this should not be a major concern for you.

window.onload = function() {
  setPathCoords();
};
window.onresize = function() {
  setPathCoords();
};

function setPathCoords() {
  var output = [],
    borderWidth = '20';
  var el = document.getElementById('percentage-pattern'),
    path = document.querySelector('#clipper2 > path'),
    origPath = 'M0,0 1,0 1,1 0,1 0,0 ';
  height = el.clientHeight;
  width = el.clientWidth;

  for (var x = 1; x < 3; x++) {
    point1 = (borderWidth * x) / width + "," + (borderWidth * x) / height;
    point2 = (borderWidth * x) / width + "," + (height - (borderWidth * x)) / height;
    point3 = (width - (borderWidth * x)) / width + "," + (height - (borderWidth * x)) / height;
    point4 = (width - (borderWidth * x)) / width + "," + (borderWidth * x) / height;

    output.push(point1);
    output.push(point2);
    output.push(point3);
    output.push(point4);
    output.push(point1);
  }
  document.querySelector('#clipper2 > path').setAttribute('d', origPath + output.join(' ') + 'z');
}
div {
  height: 200px;
  width: 100%;
  background: url(http://lorempixel.com/800/200/abstract/6);
}
#percentage-pattern {
  -webkit-clip-path: url(#clipper);
  clip-path: url(#clipper);
}
#js-pattern {
  -webkit-clip-path: url(#clipper2);
  clip-path: url(#clipper2);
}

/* just for demo */

body {
  background: radial-gradient(circle at center, aliceblue, mediumslateblue);
  min-height: 100vh;
}
<svg width='0' height='0'>
  <defs>
    <clipPath id='clipper' clipPathUnits='objectBoundingBox'>
      <path d='M0,0 1,0 1,1 0,1 0,0 0.1,0.1 0.1,0.9 0.9,0.9 0.9,0.1 0.1,0.1 0.2,0.2 0.2,0.8 0.8,0.8 0.8,0.2 0.2,0.2z' />
    </clipPath>
    <clipPath id='clipper2' clipPathUnits='objectBoundingBox'>
      <path d='M0,0 1,0 1,1 0,1 0,0 ' />
    </clipPath>
  </defs>
</svg>

<h3>Output with JS</h3>
<div id='js-pattern'></div>
<h3>Pure SVG Clip-path</h3>
<div id='percentage-pattern'></div>
like image 6
Harry Avatar answered Oct 13 '22 23:10

Harry