评价打分组件,SVG 半颗星的解决方案!

2022-06-15 14:45:57 浏览数 (1)

作者:KUMAR HARSH 译者:前端小智 来源:blog

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。

对于一个内容服务的网站来说评价打分也是很重要的一部分,它有利于分析用户对我们的内容的喜好程序。最近,我们团需要为一个项目实现一个星级评价的组件,需求如下:

  • 性能(不能用图片)
  • 可调整的大小
  • 可访问性
  • 小数位打分(如:3.53.2
  • 使用 css 就可以直接控制样式

要达到上面的要求,经常调研,最终选择了 SVG 方案。

任务

下图是我们最终想要的效果:

我们主要的工作就是让星星可以改变其颜色,描边,大小,还可以显示半颗星星。

实现

在实现之前,我们需要有一个基础 SVG 结构,如下所示:

代码语言:javascript复制
<svg width="32" height="32" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
    <path d="..."/>
</svg>

添加 aria-label

添加 aria-label 可以使用读屏器用户能够访问这一信息。

代码语言:javascript复制
<p aria-label="Rating is 4.5 out of 5">
   <svg width="32" height="32" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
      <path d="..."/>
   </svg>
</p>

aria-label属性用来给当前元素加上的标签描述,接受字符串作为参数。是用不可视的方式给元素加label(如果被描述元素存在真实的描述元素,可使用 aria-labelledby 属性作为来绑定描述元素和被描述元素来代替)。

如何重用SVG

我们可以把上面的SVG 标签复制五次,或者提取path数据并保存在某个地方,然后在不重复代码的情况下重新使用它。我们选择后者。

首先,我们需要创建一个宽度和高度为零的SVG,这样它就不会保留空间。

代码语言:javascript复制
<svg width="0" height="0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <!-- Content -->
</svg>

在该SVG中,我们需要在<symbol>元素中包含path数据。根据MDN:

symbol 元素用于定义图形模板对象,可以通过<use>元素来实例化。

<symbol>里面的内容与图标的内容相同。另外,添加一个 id 也很重要,这样我们以后就可以引用这个 symbol

代码语言:javascript复制
<svg width="0" height="0" xmlns="http://www.w3.org/2000/svg">
    <symbol xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" id="star">
        <path d="..."/>
    </symbol>
</svg>

有了这个设置,我们现在可以用<use>元素来重用这个symbol。做法就是使用id作为href属性的值。

代码语言:javascript复制
<p class="c-rate">
    <svg class="c-icon" width="32" height="32">
      <use href="#star"></use>
    </svg>
    <svg class="c-icon" width="32" height="32">
      <use href="#star"></use>
    </svg>
    <svg class="c-icon" width="32" height="32">
      <use href="#star"></use>
    </svg>
    <svg class="c-icon" width="32" height="32">
      <use href="#star"></use>
    </svg>
    <svg class="c-icon" width="32" height="32">
      <use href="#star"></use>
    </svg>
</p>

星星的样式

有了上面的星星结构,我们现在来添加样式:

代码语言:javascript复制
<p class="c-rate">
    <svg class="c-icon active" width="32" height="32">
      <use href="#star"></use>
    </svg>
    <svg class="c-icon active" width="32" height="32">
      <use href="#star"></use>
    </svg>
    <svg class="c-icon active" width="32" height="32">
      <use href="#star"></use>
    </svg>
    <svg class="c-icon active" width="32" height="32">
      <use href="#star"></use>
    </svg>
    <svg class="c-icon" width="32" height="32">
      <use href="#star"></use>
    </svg>
</p>
代码语言:javascript复制
.c-icon {
    --star-active: #fece3c;
    --star-inactive: #6c6962;
    fill: var(--star-inactive);
}

.c-icon.active {
    fill: var(--star-active);
}

运行后的结果:

半颗星

使用 SVG后做半颗星就很容易,有两个很好的解决方案。第一个是使用<masks>,第二个是使用SVG 渐变。

使用<masks>

使用 masks 的目的是模拟擦除星星的一部分并将另一部分涂成半透明颜色的效果。

在上图中,有一个正方形和一个星星。它们的交集就是我们想要的结果。

做法如下:

  • 创建一个可重用的SVG模板
  • 添加一个 <mask> 元素,位置为x=50%
  • 将 mask 应用到星星上
代码语言:javascript复制
<!-- The reusable SVG template -->
<svg viewBox="0 0 32 32" id="star">
  <defs>
    <mask id="half">
      <rect x="50%" y="0" width="32" height="32" fill="white" />
    </mask>
    <symbol viewBox="0 0 32 32" id="star">
      <path d="..." />
    </symbol>
  </defs>
</svg>

<svg class="c-icon" width="32" height="32" viewBox="0 0 32 32">
    <use href="#star" mask="url(#half)" fill="green"></use>
</svg>

问题是,当一颗半透明的星星被遮住时,我们如何才能显示它呢? 好吧,多亏了 SVG,我们可以在 <mask> 中包含多个元素。

代码语言:javascript复制
<mask id="half">
  <rect x="0" y="0" width="32" height="32" fill="white" />
  <rect x="50%" y="0" width="32" height="32" fill="black" />
</mask>

mask 中,白色元素表示我们想要显示的内容,黑色元素表示我们想要隐藏的内容。结合在一起时,我们可以创建一个cut-out effect效果。

注意,白色矩形被定位在0,0点,而黑色矩形被定位在50%,0。下面是它的效果:

涂写的部分代表最终结果,半颗星。 现在,你的可能在想,如何添加另一个半透明的星星以使其更清晰?

通过使用比纯黑更浅的颜色,我们将得到一个半透明的效果。这意味着目前被隐藏的区域将有一个浅色的星形颜色。

代码语言:javascript复制
<mask id="half">
  <rect x="0" y="0" width="32" height="32" fill="white" />
  <rect x="50%" y="0" width="32" height="32" fill="grey" />
</mask>

到这,我们回顾一下完整的 SVG 标签。

代码语言:javascript复制
<svg style="width: 0; height: 0;" viewBox="0 0 32 32">
   <defs>
      <mask id="half">
         <rect x="0" y="0" width="32" height="32" fill="white" />
         <rect x="50%" y="0" width="32" height="32" fill="grey" />
      </mask>

      <symbol viewBox="0 0 32 32" id="star">
         <path d="..." />
      </symbol>
   </defs>
</svg>

<p class="c-rate">
   <svg class="c-icon" width="32" height="32" viewBox="0 0 32 32">
      <use href="#star" mask="url(#half)" fill="green"></use>
   </svg>
   <!-- 4 more stars -->
</p>

这样,我们就有了一个部分填充的恒星。这个解决方案的绝妙之处在于,我们不需要提供两种色调, mask 会起作用的:

事例地址:https://codepen.io/shadeed/pe...

第一种方法到这就介绍完了,我们来看第二种方法。

带有SVG渐变的半星

mask类似,我们需要在<defs>元素中定义一个渐变。

代码语言:javascript复制
<svg style="width: 0; height: 0;" viewBox="0 0 32 32">
    <defs>
        <linearGradient id="half" x1="0" x2="100%" y1="0" y2="0">
            <stop offset="50%" stop-color="#f7efc5"></stop>
            <stop offset="50%" stop-color="#fed94b"></stop>
        </linearGradient>
    </defs>
</svg>

注意,我们有两个色块,第一个代表前半部分,第二个代表浅色阴影。在这个解决方案中,我们需要手动提供两种颜色。

代码语言:javascript复制
<p class="c-rate">
    <svg class="c-icon" width="32" height="32" viewBox="0 0 32 32">
        <use href="#star" fill="url(#half)"></use>
    </svg>
</p>

事例地址:https://codepen.io/shadeed/pe...

轮廓样式

接下来我们给星星做个轮廓,这样看起来会更立体点。

SVG Mask 解决轮廓样式的问题

要添加描边,我们只需要在SVG元素中添加stroke。这将很好地工作全星。然而,对于部分的,它将被切断,因为掩码。这对完整的星星来说是很好的。然而,对于半颗,由于mask的原因,它将被遮住。

为了解决这个问题,我们需要另一个星形轮廓。可以通过复制<use>元素并删除它的 mask 来实现这一点。

代码语言:javascript复制
<p class="c-rate">
   <svg class="c-icon" width="32" height="32" viewBox="0 0 32 32">
      <use href="#star" mask="url(#half)" fill="green"></use>
      <use href="#star" fill="none" stroke="grey"></use>
   </svg>
</p>

注意,我们有两个<use>元素。一个带mask的,一个只有 stroke 的。这就是使用SVG masks 实现轮廓样式的方法。

事例地址:https://codepen.io/shadeed/pe...

SVG 渐变实现轮廓样式

对于渐变解决方案,我们不需要复制图标,因为没有mask 。我们需要做的是添加一个stroke,它就完成了。

代码语言:javascript复制
<svg style="width: 0; height: 0;" viewBox="0 0 32 32">
   <defs>
      <linearGradient id="half" x1="0" x2="100%" y1="0" y2="0">
        <stop offset="50%" stop-color="#f7efc5"></stop>
        <stop offset="50%" stop-color="#fed94b"></stop>
      </linearGradient>
   </defs>
</svg>

<p class="c-rate">
   <svg class="c-icon" width="32" height="32" viewBox="0 0 32 32">
     <use href="#star" fill="url(#half)" stroke="grey"></use>
   </svg>
</p>

事业地址:https://codepen.io/shadeed/pe...

大小

通过使用CSS变量并确保SVG具有正确的viewBox属性,我们可以轻松地调整它们的大小。

代码语言:javascript复制
.c-icon {
    width: var(--size, 24px)
    height: var(--size, 24px);
}

.c-icon--md {
    --size: 40px;
}

.c-icon--lg {
    --size: 64px;
}

~完,我是刷碗智,我要去 SPA 了,我们下期见~


原文:https://ishadeed.com/article/...

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

交流

文章每周持续更新本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,欢迎Star。

0 人点赞