是否可以只显示 SVG 图标的一半?

415*_*156 13 javascript css svg reactjs react-icons

我目前有一个评级系统,可以产生 5 颗星,可以显示一半的值。我通过使用 FontAwesome 的 Half Stars 做到了这一点,并做了一些 CSS 技巧使它们看起来像一颗星。但是我想通过想出一种方法来只显示 SVG 图标的一半来增加我的 React 和 CSS 知识。因此,我可以使用用户想要的任何图标,而不是使用半星,例如,如果您想给出 3.5 的评分,它只会显示图标的 50%。

问:你能不能只显示一个图标的一半,然后以某种方式知道用户是点击一侧还是另一侧?

这是我目前使用半星的代码作为参考

import React, { useState } from 'react'
import { FaRegStarHalf, FaStarHalf } from 'react-icons/fa'

import './styles/Rater.css'

const Rater = ({ iconCount }) => {
    const [rating, setRating] = useState(null)
    const [hover, setHover] = useState(null)
    // check if user has set a rating by clicking a star and use rating to determine icons
    const Star = rating ? FaStarHalf : FaRegStarHalf

    return (
        <div>
            {[...Array(iconCount), ...Array(iconCount)].map((icon, i) => {
                const value = (i + 1) / 2

                return (
                    <label>
                        <input
                            type='radio'
                            name='rating'
                            value={value}
                            onClick={() => {
                                console.log(`value => `, value)
                                return setRating(value)
                            }}
                        />
                        <div className='star-container'>
                            <div>
                                <Star
                                    className={i % 2 ? 'star-left' : 'star'}
                                    color={value <= (hover || rating) ? '#ffc107' : '#e4e5e9'}
                                    onMouseEnter={() => setHover(value)}
                                    onMouseLeave={() => setHover(null)}
                                />
                            </div>
                        </div>
                    </label>
                )
            })}
        </div>
    )
}

export default Rater


Run Code Online (Sandbox Code Playgroud)

Rez*_*eza 7

我写了一个代码来让你了解这个想法;如果您单击星星的右侧,它的颜色将变为蓝色,如果您单击左侧,它的颜色将变为金色。此外,最好不要使用stopPropagation和检查e.target事件。

const starIcon = document.getElementById("star");
const icon = document.getElementById("icon");
starIcon.onclick = e => {
  starIcon.style.color = "blue";
  e.stopPropagation();
};
icon.onclick = e => {
  starIcon.style.color = "gold";
}
Run Code Online (Sandbox Code Playgroud)
i {
  clip-path: inset(0 0 0 50%);
  color: gold;
}
Run Code Online (Sandbox Code Playgroud)
  <!DOCTYPE html>
  <html lang="en">

    <head>
      <meta charset="UTF-8">
      <title>Document</title>
      <link href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" rel="stylesheet">
    </head>

    <body>
      <span id="icon"><i id="star", class="fas fa-star"></i></span>
    </body>

  </html>
Run Code Online (Sandbox Code Playgroud)


Dan*_*man 5

你可以在一个 SVG 中做到

  • 我放弃了 Font-Awesome 图标
  • https://iconmeister.github.io/上的 7000 多个图标中搜索“star” (第一次加载需要一分钟)
  • 拿起星形图标与最佳d路径(净度图标集:cl-social-star-solid
  • 复制了 d 路径
  • https://yqnn.github.io/svg-path-editor/ 中的 d-path 编辑为100x100 viewBox/grid
  • 通过添加到路径使其成为M0 0h100v100h-100v-100
  • 0 0 300 100viewBox 中创建了一个新的 SVG 文件以适合 3 颗星..见下文
  • 添加了背景矩形设置金色评级width="50%"
  • 使用了3 个逆星,每个星都在 x 偏移处
  • 添加了6 个 覆盖所有半星的矩形
  • 在每个“半星”上设置内联事件
    (点击在这个 SO 片段中有效,但 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>
Run Code Online (Sandbox Code Playgroud)

星级评定组件 <star-rating stars=N >

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

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

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

  • 不使用<use>,只需用x偏移量复制所有路径和矩形
  • 鼠标悬停事件设置背景 % 颜色
  • click 显示被点击的半星的索引 (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>
Run Code Online (Sandbox Code Playgroud)

注意事项

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

所有框架都支持...除了...

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

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

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

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