Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Apply a Clipping Path to a DIV Container

I'm attempting to build a kind of progress bar, similar to what Chris Coyier does here: https://css-tricks.com/css3-progress-bars/, except that I want to apply a SVG clipping path to the surrounding div element.

For example, I want to apply a SVG clipping path to .rating-stars:

<div class="rating-stars">
    <span class="rating"></span>
</div>

You can see what I have so far here: http://codepen.io/rleichty/pen/GrWmRR

It doesn't work in Safari for some reason, and I'm also not sure how to resize the SVG clipping path (if that's even possible).

Does anyone know how I might accomplish this, or perhaps have a better solution?

like image 540
Ryan Avatar asked Jan 20 '17 05:01

Ryan


People also ask

How do I use clip path?

The path() value allows us to use an SVG path to clip a specific area. For now, the browser support is inconsistent. To make it work across different browsers, we need to use an inline SVG, and then use the url() as a value for clip-path . In CSS, we need to append the path using url() value.

Can I use clip path SVG?

Support for clip-path in SVG is supported in all browsers with basic SVG support. 1 Partial support refers to only supporting the url() syntax. 2 Partial support refers to supporting shapes and the url(#foo) syntax for inline SVG, but not shapes in external SVGs.

How do you clip content in CSS?

The <top> , <right> , <bottom> , and <left> values may be either a <length> or auto . If any side's value is auto , the element is clipped to that side's inside border edge. The element does not clip (default). This is different from rect(auto, auto, auto, auto) , which clips to the element's inside border edges.


1 Answers

Figured it out. Here's the final code here: http://codepen.io/rleichty/pen/zNZajM

HTML:

<div class='rating-container'>
    <div class='rating-stars'>
        <span class='rating'></span>
    </div>
</div>

<svg width='0' height='0'>
    <defs>
        <clipPath id='svgStars' clipPathUnits = 'objectBoundingBox'>
            <polygon points=".80 .073 .738 .118 .762 .19 .70 .145 .638 .19 .662 .118 .60 .073 .538 .118 .562 .19 .50 .145 .438 .19 .462 .118 .40 .073 .338 .118 .362 .19 .30 .145 .238 .19 .262 .118 .20 .073 .138 .118 .162 .19 .10 .145 .038 .19 .062 .118 0 .073 .076 .073 .10 0 .124 .073 .276 .073 .30 0 .324 .073 .476 .073 .50 0 .524 .073 .676 .073 .70 0 .724 .073 .876 .073 .90 0 .924 .073 1 .073 .938 .118 .962 .19 .90 .145 .838 .19 .862 .118 "/>
        </clipPath>
    </defs>
</svg>

CSS:

$ratingHeight: 100px;

.rating-container {
    position: relative;
    width: calc((100 / 19) * #{$ratingHeight});
    height: $ratingHeight;
}

.rating-stars {
    position: absolute;
    width: 100%;
    height: 0%;
    padding-bottom: 100%;
    background: lightgray;
    -webkit-clip-path: url(#svgStars);
    clip-path: url(#svgStars);
    -webkit-clip-path: polygon(80% 7.3%, 73.8% 11.8%, 76.2% 19%, 70% 14.5%, 63.8% 19%, 66.2% 11.8%, 60% 7.3%, 53.8% 11.8%, 56.2% 19%, 50% 14.5%, 43.8% 19%, 46.2% 11.8%, 40% 7.3%, 33.8% 11.8%, 36.2% 19%, 30% 14.5%, 23.8% 19%, 26.2% 11.8%, 20% 7.3%, 13.8% 11.8%, 16.2% 19%, 10% 14.5%, 3.8% 19%, 6.2% 11.8%, 0 7.3%, 7.6% 7.3%, 10% 0, 12.4% 7.3%, 27.6% 7.3%, 30% 0, 32.4% 7.3%, 47.6% 7.3%, 50% 0, 52.4% 7.3%, 67.6% 7.3%, 70% 0, 72.4% 7.3%, 87.6% 7.3%, 90% 0, 92.4% 7.3%, 100% 7.3%, 93.8% 11.8%, 96.2% 19%, 90% 14.5%, 83.8% 19%, 86.2% 11.8%);
    clip-path: polygon(80% 7.3%, 73.8% 11.8%, 76.2% 19%, 70% 14.5%, 63.8% 19%, 66.2% 11.8%, 60% 7.3%, 53.8% 11.8%, 56.2% 19%, 50% 14.5%, 43.8% 19%, 46.2% 11.8%, 40% 7.3%, 33.8% 11.8%, 36.2% 19%, 30% 14.5%, 23.8% 19%, 26.2% 11.8%, 20% 7.3%, 13.8% 11.8%, 16.2% 19%, 10% 14.5%, 3.8% 19%, 6.2% 11.8%, 0 7.3%, 7.6% 7.3%, 10% 0, 12.4% 7.3%, 27.6% 7.3%, 30% 0, 32.4% 7.3%, 47.6% 7.3%, 50% 0, 52.4% 7.3%, 67.6% 7.3%, 70% 0, 72.4% 7.3%, 87.6% 7.3%, 90% 0, 92.4% 7.3%, 100% 7.3%, 93.8% 11.8%, 96.2% 19%, 90% 14.5%, 83.8% 19%, 86.2% 11.8%);
}

.rating {
    position: absolute;
    display: block;
    width: 50%;
    height: 100%;
    background-color: orange;
}

You can adjust the height of the stars by adjusting this SCSS variable $ratingHeight: 100px; You don't need to use the variable, but it makes it simpler to adjust only one value (you could also flip it around so that the width is prioritized).

I found a lot of help here https://sarasoueidan.com/blog/css-svg-clipping/ as she notes:

One important thing to note here is that when you use the objectBoundingBox value, the coordinates specified for the contents of the <clipPath> must be in the range [0, 1]—the coordinate system becomes a unit system, and the coordinates of the shapes inside a clipPath have to be fractions in that range.

So I had to adjust the SVG coordinates to fractions.

In order to keep the aspect ratio of a percentage based clipping path, I found help out here How do I keep the aspect ratio of a percentage based clipping path? and simply used the padding-bottom trick.

Now all you need to do is use jQuery to dynamically set the width of the .rating element to represent a star rating. In my case, I'll be pulling data from Goodreads.

Personally I like this method as opposed to having separate star images for every single rating. It's simpler and more lightweight, as well as being vector based and responsive.

like image 182
Ryan Avatar answered Oct 06 '22 07:10

Ryan