I am creating a component similar to MudRating to display the average of the ratings an item is given. So unlike MudRating which only allows integer values of 1 - 5, this will generally get a rating that's a double. For example 3.21 or 4.88.
At present MudBlazor only has empty, half, and full star. So the ratings have to be displayed as:
And these are rendered (RatingAverage.razor) as:
@for (var i = 0; i < numFullStars; i++)
{
<MudIcon Icon="@Icons.Material.Filled.Star" Color="Color.Warning" />
}
@if (showHalfStar)
{
<MudIcon Icon="@Icons.Material.Filled.StarHalf" Color="Color.Warning" />
}
@for (var i = startEmptyStars; i < 5; i++)
{
<MudIcon Icon="@Icons.Material.Filled.StarOutline" Color="Color.Warning" />
}
Here's my question. Is there a way to render the partial star as:
<MudIcon Icon="@Icons.Material.Filled.StarOutline" Color="Color.Warning" />
<MudIcon Crop="0,0, @width ,1" Position="OnTopStarOutline" Icon="@Icons.Material.Filled.Star" Color="Color.Warning" />
where:
Position="OnTopStarOutline"
(clearly needs to be something else) will render the Star
on top of the StarOutline
@width
is a value from 0.0 to 1.0 that crops the Star
to that percentage of the width, so it only overwrites the leftmost of StarOutline
it is over.Is there a way to accomplish this? And if so, how? And I assume this is a CSS question. But if there's another way, please enlighten me.
Use clip path. I can't use MudIcon in snippet so these round <span>
s are only for demonstration. In your code just set --width
and apply the clip-path
on your icons:
.icon {
display: inline-block;
width: 1.5em;
height: 1.5em;
position: relative;
}
.icon::before {
content: '';
position: absolute;
inset: 0;
display: block;
border: 1px solid red;
border-radius: 50%;
}
.icon::after {
content: '';
position: absolute;
inset: 0;
display: block;
background: red;
border-radius: 50%;
clip-path: inset(0 calc(100% - var(--width, 0%)) 0 0);
}
<span class="icon" style="--width:10%"></span>
<span class="icon" style="--width:20%"></span>
<span class="icon" style="--width:30%"></span>
<span class="icon" style="--width:40%"></span>
<span class="icon" style="--width:50%"></span>
<span class="icon" style="--width:60%"></span>
<span class="icon" style="--width:70%"></span>
<span class="icon" style="--width:80%"></span>
<span class="icon" style="--width:90%"></span>
Edit: More explanation
The clip-path
CSS property is a way to visually limit an element to a specific region, sort of like a stencil. It's very similar to the <clipPath>
element in SVG, but since it's an CSS property, it can access CSS variables (aka custom properties). So I also combine it with --width
(a custom property, you can name it whatever you want) to dynamically construct a clip rectangle based on the desired width.
inset()
means distant from 4 edges of its original box. It has similar syntax to other box-related property, so the calc(100% - var(--width, 0%))
means distant from right edge equals 100% minus --width
(if --width
is missing, default to 0%).
Check the linked MDN page, they have more details and examples.