使用 Css 部分填充 SVG 图标
Posted
技术标签:
【中文标题】使用 Css 部分填充 SVG 图标【英文标题】:Partially Fill SVG icon using Css 【发布时间】:2021-12-23 21:44:06 【问题描述】:是否可以根据某些值部分填充 svg 图标? 用例:星级
svg
fill: red;
<!DOCTYPE html>
<html lang="en">
<title></title>
<script src="https://unpkg.com/feather-icons"></script>
<body>
<!-- example icon -->
<i data-feather="star"></i>
<i data-feather="star"></i>
<i data-feather="star"></i>
<script>
feather.replace()
</script>
</body>
</html>
【问题讨论】:
【参考方案1】:假设您不能直接编辑原始路径,您可以操作 SVG 的 dom 以插入带有 ID 的渐变标签,然后在 CSS 中使用 fill : url(#gradient);
引用它。这似乎有点hacky,但它有效:
svg.full
fill:red;
svg#half
fill : url(#gradient);
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<script src="https://unpkg.com/feather-icons"></script>
</head>
<body>
<!-- example icon -->
<i class="full" data-feather="star"></i>
<i id="half" data-feather="star"></i>
<i data-feather="star"></i>
<script type="text/javascript">
feather.replace();
// create the defs SVG tag
// it is important to use createElementNS in that case
const defs = document.createElementNS("http://www.w3.org/2000/svg", 'defs');
defs.innerHTML = '<linearGradient id="gradient"><stop offset="50%" stop-color="#F00"></stop><stop offset="50%" stop-color="transparent" ></stop></linearGradient>';
// Here I'm targeting only the middle start by ID
// You should modify the code to target all half-stars
document.getElementById('half').append(defs);
</script>
</body>
</html>
PS:玩<stop>
标签属性,可以实现一些原创效果。
【讨论】:
【参考方案2】:您可以在 ONE SVG 中做到这一点
我放弃了 Font-Awesome 图标 在https://iconmeister.github.io/ 的 7000 多个图标中搜索“星号”(首次加载需要一分钟) 选择具有最佳 d-path 的星形图标(Clarity Iconset:cl-social-star-solid
)
只复制了 d-path
将https://yqnn.github.io/svg-path-editor/ 中的 d-path 编辑为 100x100
视图框/网格
通过在路径前添加M0 0h100v100h-100v-100
使其成为逆星
在0 0 300 100
viewBox 中创建了一个新的 SVG 文件以适合 3 星。见下文
添加了一个背景矩形设置金色颜色等级width="50%"
使用 3 颗逆星,每颗都在 x 轴上
添加了 6 个矩形 覆盖所有半星
在每个“半星”上设置内联事件
(点击在这个 SO sn-p 中有效,但 SO 增加了很长的延迟)
概念证明
<svg viewBox="0 0 300 100" >
<rect id="rating" fill="gold" />
<path id="star" fill="green"
d="M0 0h100v100h-100v-100m91 42a6 6 90 00-4-10l-22-1a1 1 90 01-1
0l-8-21a6 6 90 00-11 0l-8 21a1 1 90 01-1 1l-22 1a6 6 90 00-4
10l18 14a1 1 90 010 1l-6 22a6 6 90 008 6l19-13a1 1 90 011 0l19
13a6 6 90 006 0a6 6 90 002-6l-6-22a1 1 90 010-1z"/>
<use href="#star" x="100" />
<use href="#star" x="200" />
<rect id="c" fill="transparent" stroke="red"
onclick="console.log(this)" />
<use href="#c" x="50" />
<use href="#c" x="100" />
<use href="#c" x="150" />
<use href="#c" x="200" />
<use href="#c" x="250" />
</svg>
星级组件<star-rating stars=N >
您不想手动创建所有这些 SVG...几行 JavaScript 可以创建 SVG,任意数量的星星
这里使用W3C标准的Web Component,因为它运行在这个页面中,不像React Component那么复杂。
https://developer.mozilla.org/en-US/docs/Web/Web_Components
不使用<use>
,只需使用x
偏移量复制所有路径和矩形
鼠标悬停事件设置背景 % 颜色
点击显示被点击的半星的索引(0+)
评分可以设置为或百分比值; document.querySelector('[stars="5"]').rating="90%"
(4.5 颗星)
您的用例可能需要额外的工作
所有必需的 HTML 和 JavaScript:
<star-rating stars=5 rating="3.5"
bgcolor="green" nocolor="grey" color="gold"></star-rating>
<star-rating stars=7 rating="50%"
bgcolor="rebeccapurple" nocolor="beige" color="goldenrod"></star-rating>
<script>
document.addEventListener("click", (evt) => console.log(evt.target.getAttribute("n")))
customElements.define("star-rating", class extends HTMLElement
set rating( rate )
if (!String(rate).includes("%")) rate = Number(rate) / this.stars * 100 + "%";
this.querySelector("#rating").setAttribute("width", rate);
connectedCallback()
let bgcolor, stars, nocolor, color, rating = this.attributes;
this.stars = ~~stars.value || 5;
this.innerHTML =
`<svg viewBox="0 0 $this.stars*100 100" style="cursor:pointer;width:300px">`
+ `<rect fill="$nocolor.value"/>`
+ `<rect id="rating" fill="$color.value" />`
+ Array( this.stars ).fill()
.map((i, n) => `<path fill="$bgcolor.value" d="M$ n*100 0h102v100h-102v-100m91 42a6 6 90 00-4-10l-22-1a1 1 90 01-1 0l-8-21a6 6 90 00-11 0l-8 21a1 1 90 01-1 1l-22 1a6 6 90 00-4 10l18 14a1 1 90 010 1l-6 22a6 6 90 008 6l19-13a1 1 90 011 0l19 13a6 6 90 006 0a6 6 90 002-6l-6-22a1 1 90 010-1z"/>`)
.join("")
+ Array( this.stars * 2 ).fill()
.map((i, n) => `<rect x="$ n*50 " n="$n" opacity="0" `
+ ` onclick="dispatchEvent(new Event('click'))" `
+ ` onmouseover="this.closest('star-rating').rating = $(n+1)/2"/>`)
.join("")
+ "</svg>";
this.rating = rating.value;
);
</script>
注意事项:
这个native<star-rating>
组件(也称为自定义元素,因为不涉及shadowDOM)具有零依赖
没有库
没有外部 SVG
原生组件不是自闭合标签,必须包含连字符,因此表示法为:<star-rating></star-rating>
将星号更改为 M0 0h102v100h-102v-100
(2 像素重叠)以解决 SVG 舍入问题
在所有框架中都受支持...除了...
React 还不支持这种现代 W3C Web 组件标准。
https://custom-elements-everywhere.com/ 上的 React 分数仅为 71%
所有其他框架(Angular、Vue、Svelte)都有100%支持
你必须做一些额外的工作来处理 React 中的原生 DOM 元素和事件;但自定义元素并不复杂.. 它创建 SVG;应该很容易复制为 React 组件。
【讨论】:
以上是关于使用 Css 部分填充 SVG 图标的主要内容,如果未能解决你的问题,请参考以下文章