【问题标题】:Partially Fill SVG icon using Css使用 Css 部分填充 SVG 图标
【发布时间】: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>

【问题讨论】:

    标签: css svg icons fill


    【解决方案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:玩&lt;stop&gt;标签属性,可以实现一些原创效果。

    【讨论】:

      【解决方案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" width="500px">
        <rect id="rating" width="50%" fill="gold" height="100%" />
      
        <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" width="16.66%" height="100%" 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>

      星级组件&lt;star-rating stars=N &gt;

      您不想手动创建所有这些 SVG...几行 JavaScript 可以创建 SVG,任意数量的星星

      这里使用W3C标准的Web Component,因为它运行在这个页面中,不像React Component那么复杂。

      https://developer.mozilla.org/en-US/docs/Web/Web_Components

      • 不使用&lt;use&gt;,只需使用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 width="100%" height="100" fill="${nocolor.value}"/>`
            + `<rect id="rating"  height="100" 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" width="50" height="100"`
                        + ` onclick="dispatchEvent(new Event('click'))" `
                        + ` onmouseover="this.closest('star-rating').rating = ${(n+1)/2}"/>`)
                    .join("") 
            + "</svg>";
      
            this.rating = rating.value;
          }
        });
      </script>

      注意事项

      • 这个native&lt;star-rating&gt;组件(也称为自定义元素,因为不涉及shadowDOM)具有零依赖
        • 没有库
        • 没有外部 SVG
      • 原生组件不是自闭合标签,必须包含连字符,因此表示法为:&lt;star-rating&gt;&lt;/star-rating&gt;
      • 将星号更改为 M0 0h102v100h-102v-100(2 像素重叠)以解决 SVG 舍入问题

      在所有框架中都受支持...除了...

      React 还不支持这种现代 W3C Web 组件标准

      https://custom-elements-everywhere.com/ 上的 React 分数仅为 71%

      所有其他框架(Angular、Vue、Svelte)都有100%支持

      你必须做一些额外的工作来处理 React 中的原生 DOM 元素和事件;但自定义元素并不复杂.. 它创建 SVG;应该很容易复制为 React 组件。

      【讨论】:

        猜你喜欢
        • 2021-03-06
        • 1970-01-01
        • 2015-04-17
        • 2019-10-22
        • 2023-04-05
        • 2022-08-22
        • 2017-02-27
        • 2019-10-26
        相关资源
        最近更新 更多