作者:KUMAR HARSH
译者:前端小智
来源:blog
有梦想,有干货,微信搜索 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。
本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。
对于一个内容服务的网站来说评价打分也是很重要的一部分,它有利于分析用户对我们的内容的喜好程序。最近,我们团需要为一个项目实现一个星级评价的组件,需求如下:
- 性能(不能用图片)
- 可调整的大小
- 可访问性
- 小数位打分(如:
3.5
或3.2
) - 使用 css 就可以直接控制样式
要达到上面的要求,经常调研,最终选择了 SVG 方案。
任务
下图是我们最终想要的效果:
我们主要的工作就是让星星可以改变其颜色,描边,大小,还可以显示半颗星星。
实现
在实现之前,我们需要有一个基础 SVG 结构,如下所示:
<svg width="32" height="32" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<path d="..."/>
</svg>
添加 aria-label
添加 aria-label
可以使用读屏器用户能够访问这一信息。
<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,这样它就不会保留空间。
<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
。
<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
属性的值。
<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>
星星的样式
有了上面的星星结构,我们现在来添加样式:
<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>
.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 应用到星星上
<!-- 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>
中包含多个元素。
<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
。下面是它的效果:
涂写的部分代表最终结果,半颗星。 现在,你的可能在想,如何添加另一个半透明的星星以使其更清晰?
通过使用比纯黑更浅的颜色,我们将得到一个半透明的效果。这意味着目前被隐藏的区域将有一个浅色的星形颜色。
<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 标签。
<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/pen/bGWyoXW
第一种方法到这就介绍完了,我们来看第二种方法。
带有SVG渐变的半星
与mask
类似,我们需要在
<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)"></use>
</svg>
</p>
事例地址:https://codepen.io/shadeed/pen/eYWaero
轮廓样式
接下来我们给星星做个轮廓,这样看起来会更立体点。
SVG Mask 解决轮廓样式的问题
要添加描边,我们只需要在SVG元素中添加stroke
。这将很好地工作全星。然而,对于部分的,它将被切断,因为掩码。这对完整的星星来说是很好的。然而,对于半颗,由于mask
的原因,它将被遮住。
为了解决这个问题,我们需要另一个星形轮廓。可以通过复制<use>
元素并删除它的 mask
来实现这一点。
<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/pen/jOmoaQQ
SVG 渐变实现轮廓样式
对于渐变解决方案,我们不需要复制图标,因为没有mask
。我们需要做的是添加一个stroke
,它就完成了。
<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/pen/QWvROVV
大小
通过使用CSS变量并确保SVG具有正确的viewBox
属性,我们可以轻松地调整它们的大小。
.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/star-rating-svg/
代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。
交流
文章每周持续更新,可以微信搜索**【大迁世界 】第一时间阅读,回复【福利】**有多份前端视频等着你,本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,欢迎Star。