动画:
移入到某个星星位置,之前的星星高亮变大,后面的星星暗色不变
实现:
动画:
(1)通过一个索引,索引值为-1,每当移入到星星时,索引变成当前索引,当星星自身的索引小于等于改索引时,添加样式高亮,
(2)移出所有星星时,重新将索引设置为-1,使得星星保持初始样式,同时为了在移动过程覆盖初始样式,移出所有星星时还原初始样式,还要在显示初始样式时,添加索引值是否为-1的判断条件
根据评分的小数,显示星星的高亮面积:
(1)在暗色星星的基础上,再定位一个大小相同的高亮星星,高亮星星的容器宽度为小数所占的百分比,然后设置overflow:hidden
参数:
rate: PropTypes.number, 初始评分,0-5
showRate: PropTypes.bool, 是否显示评分,默认显示
onChange: PropTypes.func, 在非只读的模式下,点击星星改变评分回调,第一个参数为当前评分
readOnly: PropTypes.bool, 是否是只读模式(无移动交互等),默认为否
iconSize: PropTypes.string, 星星大小
iconColor: PropTypes.string, 星星未选中的颜色
activeColor: PropTypes.string, 星星选中的颜色
fontSize: PropTypes.string, 评分字体大小
fontColor:PropTypes.string, 评分字体颜色
效果图:
非只读:
只读:
代码示例:
使用:
import React, { useState } from 'react';
import Rate from './rate/rate';
function App(){
const _onChange = function (rate)
{
console.log(rate);
}
return(
<div>
<Rate rate={3.2} onChange={_onChange}></Rate>
</div>
)
}
export default App
rate.jsx:
import React, { useState, useCallback, useEffect, useMemo,useRef } from 'react';
import PropTypes from 'prop-types';
import './rate.css';
function App(props){
const [rate, setRate] = useState(0);
const [rateArr, setRateArr] = useState([]);
const [dynamicIndex, setDynamicIndex] = useState(-1);
const [currentIndex, setCurrentIndex] = useState(-1);
const ratef = useRef(null);
useEffect(() => {
//初始rate
setRate(props.rate);
//处理rate数据
_handleRate(props.rate);
//设置样式
const { iconSize, iconColor, activeColor, fontSize, fontColor } = props;
iconSize && _setAttribute('iconSize', iconSize);
iconColor && _setAttribute('iconColor', iconColor);
activeColor && _setAttribute('activeColor', activeColor);
fontSize && _setAttribute('fontSize', fontSize);
fontColor && _setAttribute('fontColor', fontColor);
}, [props.rate])
const _setAttribute=function(key,value)
{
ratef.current.style.setProperty('--' + key, value);
}
//处理rate数据
const _handleRate=useCallback(function(rate)
{
let arr = [];
let one = Math.floor(rate);
let dynamic = Number((rate - one).toFixed(1));
for (let i = 0; i < one; i++)
{
arr.push(1);
}
if (dynamic > 0 && dynamic < 1)
{
setDynamicIndex(one);
arr.push(dynamic);
}
while (arr.length != 5)
{
arr.push(0);
}
setRateArr(arr);
},[])
//移动
const _onMouseMove = useCallback(function (index) {
if (index != currentIndex&&!props.readOnly) {
setCurrentIndex(index);
}
}, [currentIndex]);
//移出
const _onMouseOut = useCallback(function () {
!props.readOnly&&setCurrentIndex(-1);
}, []);
//点击
const _onClick = useCallback(function (index) {
if (props.readOnly)
{
return ;
}
_handleRate(index + 1);
setRate(index + 1);
props.onChange(index + 1);
}, []);
return(
<div ref={ratef} className='jf-rate-container'>
<div className='jf-rate-stars' onMouseOut={_onMouseOut}>
{
rateArr.map((item, index) => {
return (
<span onClick={()=>{_onClick(index)}} onMouseMove={() => { _onMouseMove(index) }} className='jf-rate-item' key={item+index*2}>
<i className={`jf-rate-item-star iconfont icon-Starlarge ${item == 1 &¤tIndex==-1&& 'jf-rate-item-star-on'} ${(index<=currentIndex)&&'jf-rate-item-star-on-hover'} `}>
{dynamicIndex==index&¤tIndex==-1&&<i className={`jf-rate-item-star-dynamic iconfont icon-Starlarge `} style={{width:item*100+'%'}}></i>}
</i>
</span>
)
})
}
</div>
{props.showRate&&<span className='jf-rate-rate' >{rate}</span>}
</div>
)
}
App.propTypes = {
rate: PropTypes.number,
showRate: PropTypes.bool,
onChange: PropTypes.func,
readOnly: PropTypes.bool,
iconSize: PropTypes.string,
iconColor: PropTypes.string,
activeColor: PropTypes.string,
fontSize: PropTypes.string,
fontColor:PropTypes.string,
}
App.defaultProps = {
rate: 0,
onChange: () => { },
showRate:true
}
export default React.memo(App);
rate.css:
.jf-rate-container {
display: inline-block;
box-sizing: border-box;
display: flex;
align-items: center;
--iconSize: 17px;
--iconColor: #eff2f7;
--activeColor: #f7ba2a;
--fontSize: 14px;
--fontColor: #f7ba2a;
}
.jf-rate-item-star {
display: inline-block;
color: var(--iconColor);
position: relative;
font-size: var(--iconSize);
transition: 0.3s;
}
.jf-rate-item-star-on {
color: var(--activeColor);
}
.jf-rate-item-star-on-hover {
color: var(--activeColor);
transform: scale(1.15);
}
.jf-rate-item-star-dynamic {
position: absolute;
left: 0;
top: 0;
color: var(--activeColor);
overflow: hidden;
font-size: var(--iconSize);
}
.jf-rate-rate {
color: var(--fontColor);
font-size: var(--fontSize);
font-weight: bold;
}