几行代码 基于SVG+CSS clip-path 快速实现星级评分

前言

虽然星级评分的前端实现已经有很多种方案了,但是对于快速(1h内,从0-1)去了解实现此效果,那些案例涉及的知识点就比较多。于是只能在有限的时间内 利用空间换时间,用额外的div来实现星级评分效果。

太长不看版:codepen

效果


要实现上述的效果需要先做基本的拆解

  1. 星星有填充的 也有空心的
  2. 星星有填充一半的,也有填充10%的,此渲染应为动态

了解clip-path

MDN

clip-path CSS 属性使用裁剪方式创建元素的可显示区域。区域内的部分显示,区域外的隐藏。

通俗来理解就是一个白色的长方形的div 通过设置clip-path可以变成星型的不规则的div

找个svg图标

明白了clip-path属性后,下一步就找个svg图标。

https://icons.getbootstrap.com/

这里有三个icon,我们选择Star fill 原因下面来讲

懒得去翻页的可以直接看下述的SVG代码

<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-star-fill" viewBox="0 0 16 16">
  <path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>

了解svg的clipPath与css中的clip-path

  1. css的clip-path属性可以引用svg中的clipPath
  2. css的clip-path属性可以直接用path函数生成(兼容性有坑)

https://developer.mozilla.org/zh-CN/docs/Web/SVG/Element/clipPath

我们先用第二种来绘制

<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-star-fill" id="svgx" viewBox="0 0 16 16">
  <clipPath id='svgclip'>
    <path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
  </clipPath>  
</svg>

<div class="s1"></div>
.s1{
  width:16px;
  height:16px;
  background:red;
  clip-path:path('M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z');
}

这样是快速实现了一个小星星,如果你把这段代码在移动端执行的话会发现兼容性的问题(安卓12+微信浏览器,ios未测试)

初步看caniuse也看不出path函数的兼容性问题.所以在实现的时候只能通过svg的clipPath来实现了

.s1{
  width:100px;
  height:100px;
  background:red;
  clip-path:url('#svgclip');
}

tips: svg的clipPath会截取掉其他内容,从而导致一个svg元素内部内容不可见(被裁剪了),实际上svg元素还是占位的。
div的width和height与svg的width、height保持一致性,裁剪的宽度是依赖svg的,为了避免不必要的额外宽高度占位。

实现宽度可变化的星

.s1{
  display:inline-block;
  width:8px;
  height:16px;
  background:red;
  clip-path: url('#svgclip');
}

只需要调整div的高度 即可实现一个宽度的变化,那么,到这里就已经完善了么?
注意看我们要实现的效果其实有个空心的星边框,而我们绘制出来的div是实心的

实现一个空心的星

由于我们选择的是Star fill,从实星变成空心的只需要利用到svg的stroke属性

<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="transparent" class="bi bi-star-fill" stroke="red" viewBox="0 0 16 16">
 <path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg>

如果用的是空心的图标要实现反转填充就比较麻烦。

这样得到的效果就是一个空星,注意我们把fill="transparent"设置为透明,边框stroke="red"设置为红色

通过position定位整合div和svg

<div class="wrap">
  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="transparent" class="bi bi-star-fill" stroke="red" viewBox="0 0 16 16">
    <path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z" />
  </svg>
  <div class="s1">
  </div>
</div>
#svgx {
  position: absolute;
  z-index: -99;
}

.wrap {
  position: relative;
  display: inline-flex;
}

.s1 {
  position: absolute;
  left: 0;
  top: 0px;
  display: inline-flex;
  width: 70%;
  height: 100%;
  background: blue;
  clip-path: url("#svgclip");
}

我们可以动态的控制div的宽度来展示星级的填充度,设置inline-flex而不是inline-block的原因是避免font的空白。

详情可以见codepen