В настоящее время у меня есть рейтинговая система, которая дает 5 звезд, которые могут показывать половинные значения. Я сделал это с помощью Half Stars от FontAwesome и применил несколько трюков CSS, чтобы они выглядели как одна звезда. Но я думал о том, чтобы улучшить свои знания React и CSS, придумав способ отображать только половину значка SVG. Таким образом, вместо использования Half Stars я мог использовать любой значок, который хотел пользователь, и он отображал бы только 50% значка, например, если вы хотели дать оценку 3,5.
В: Можете ли вы показать только половину значка и каким-то образом узнать, нажимает ли пользователь на одну сторону или на другую?
Вот код, который у меня есть в настоящее время, который использует HalfStars для небольшого справки
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.info(`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
Этот ответ может быть полезен: stackoverflow.com/questions/29368138/…
Вы можете использовать CSS clip-path, чтобы замаскировать звезду, а затем проверить в обработчике клика, находится ли координата клика на первой или второй половине значка.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я написал код, чтобы дать вам представление; Если щелкнуть по правой стороне звезды, ее цвет изменится на синий, а если щелкнуть по левой стороне, ее цвет изменится на золотой. Также лучше не использовать 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";
}i {
clip-path: inset(0 0 0 50%);
color: gold;
} <!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>Вот другая идея с использованием 3D-преобразования:
i {
color: gold;
}
.container {
background: #fff;
transform-style: preserve-3d;
}
.half {
transform: rotateY(1deg);
}
i.fa-star:hover {
color:red;
}<link href = "https://pro.fontawesome.com/releases/v5.10.0/css/all.css" rel = "stylesheet">
<div class = "container">
<i id = "star" class = "fas fa-star fa-5x"></i>
<i id = "star" class = "fas fa-star fa-5x"></i>
<i id = "star" class = "fas fa-star fa-5x"></i>
<i id = "star" class = "fas fa-star fa-5x half"></i>
</div>
<div class = "container">
<i id = "star" class = "fas fa-star fa-5x"></i>
<i id = "star" class = "fas fa-star fa-5x half"></i>
</div>Вы можете сделать это в ОДНОМ SVG
cl-social-star-solid)M0 0h100v100h-100v-100 к пути0 0 300 100 viewBox, чтобы он соответствовал 3 звездам.. см. нижеwidth = "50%"<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.info(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, потому что он работает на этой странице и не такой сложный, как компонент React.
https://developer.mozilla.org/en-US/docs/Web/Web_Components
<use>, просто дублируйте все пути и прямоугольники со смещением xdocument.querySelector('[stars = "5"]').rating = "90%" (4,5 звезды)<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.info(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>Примечания:
<star-rating> (также называемый настраиваемым элементом, потому что НИКАКОЙ shadowDOM не задействован) не имеет зависимостей ZERO.
<star-rating></star-rating>M0 0h102v100h-102v-100 (перекрытие 2 пикселя), чтобы скрыть проблемы с округлением SVG.React пока не поддерживает этот современный стандарт веб-компонентов W3C.
React набирает всего 71% на https://custom-elements-everywhere.com/
Все остальные фреймворки (Angular, Vue, Svelte) поддерживаются на 100%.
Вам нужно проделать дополнительную работу для обработки собственных элементов DOM и событий в React; но пользовательский элемент не сложен. Он создает SVG; должно быть легко воспроизведено как компонент React.
Самое простое решение, которое я могу придумать, — это иметь ряд значков svg, а затем использовать overflow:hidden, чтобы обрезать выбор пользователя.
Затем, используя половину ширины значка, он может идти с шагом в половину.
$(document).ready(function()
{
$(document).on("mousemove", ".star-container.unvoted", function(e)
{
let width = $(this).find(".star").width() / 2;
e.clientX = Math.ceil(e.clientX /= (width)) * width;
$(this).find(".inner").css("width", e.clientX + "px");
});
$(".star-container.unvoted").click(function(e)
{
$(this).removeClass("unvoted");
});
});.star-container .inner{
overflow: hidden;
width:100%;
height:100%;
}
.star-container .inner .star{
display:inline-block;
width:20px;
height:20px;
background-image:url('https://www.flaticon.com/svg/static/icons/svg/1828/1828884.svg');
background-size:contain;
}
.star-container .inner .inner-2{
width:10000px;
}
<script src = "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class = "star-container unvoted">
<div class = "inner">
<div class = "inner-2">
<div class = "star"></div><div class = "star"></div><div class = "star"></div><div class = "star"></div><div class = "star"></div>
</div>
</div>
</div>
Если вы хотите иметь возможность отображать половину звезды и определять, какая половина была нажата, самый простой способ — показать (половину) звезды в качестве фонового изображения тега
<a>. Это означает, что вам также придется установить ширину тега, чтобы отображалась только половина SVG, поэтому вам, возможно, придется изменить свойство тегаdisplayнаinline-block.