重构-五星评分

动画:
	移入到某个星星位置,之前的星星高亮变大,后面的星星暗色不变

实现:
	动画:
	(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 &&currentIndex==-1&& 'jf-rate-item-star-on'} ${(index<=currentIndex)&&'jf-rate-item-star-on-hover'} `}>
                 {dynamicIndex==index&&currentIndex==-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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值