重构-五星评分
動畫:移入到某個星星位置,之前的星星高亮變大,后面的星星暗色不變實現:動畫:(1)通過一個索引,索引值為-1,每當移入到星星時,索引變成當前索引,當星星自身的索引小于等于改索引時,添加樣式高亮,(2)移出所有星星時,重新將索引設置為-1,使得星星保持初始樣式,同時為了在移動過程覆蓋初始樣式,移出所有星星時還原初始樣式,還要在顯示初始樣式時,添加索引值是否為-1的判斷條件根據評分的小數,顯示星星的高亮面積:(1)在暗色星星的基礎上,再定位一個大小相同的高亮星星,高亮星星的容器寬度為小數所占的百分比,然后設置overflow:hidden參數:rate: PropTypes.number, 初始評分,0-5showRate: PropTypes.bool, 是否顯示評分,默認顯示onChange: PropTypes.func, 在非只讀的模式下,點擊星星改變評分回調,第一個參數為當前評分readOnly: PropTypes.bool, 是否是只讀模式(無移動交互等),默認為否iconSize: PropTypes.string, 星星大小iconColor: PropTypes.string, 星星未選中的顏色activeColor: PropTypes.string, 星星選中的顏色fontSize: PropTypes.string, 評分字體大小fontColor:PropTypes.string, 評分字體顏色
效果圖:
非只讀:
只讀:
代碼示例:
使用:
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(() => {//初始ratesetRate(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; }總結
- 上一篇: VIVO NEX 3 5G版上手评测,除
- 下一篇: vue3+Typescript---Co