各种偏门捷径实现如图所示的评分组件
一、低配版
const rate = (num) =>"★★★★★☆☆☆☆☆".substring(5 - num, 10 - num);
没错,只需一行代码就能实现评分(狗头)
虽然星星的样式一言难尽,也没有星星的交互,但这种实现方式你想到过吗?
二、标准版
rc-rate 的实现思路,ant-design 就是用的这个 rate 组件
首先实现单个星星 Star 组件
import React from 'react'; export default class Star extends React.Component { onHover = e => { const { onHover, index } = this.props; onHover(e, index); }; onClick = e => { const { onClick, index } = this.props; onClick(e, index); }; getClassName() { // 根据当前评分修改 star 的 class
const { index, value } = this.props; const starValue = index + 1; let className = starValue <= value ? 'full' : 'zero'; return className; } render() { const { onHover, onClick } = this; const { index, count, value, character } = this.props; // character 用于自定义星星图标
const characterNode = typeof character === 'function' ? character(this.props) : character; const start = ( <li className={this.getClassName()}>
<div onClick={onClick} onMouseMove={onHover} role="radio" aria-checked={value > index ? 'true' : 'false'} aria-posinset={index + 1} aria-setsize={count} > {/* 如果要做半星,就把 characterNode 拆成两个 div */} {characterNode} </div>
</li> ); return start; } }
在 Star 组件中,暴露出 onClick 和 onHover 事件
然后基于当前评分 value 和当前位置 index 来切换自身的 class,以实现普通状态和高亮状态
然后是 Rate 组件:
import React from 'react'; import Star from './star'; export default class Rate extends React.Component { constructor(props) { super(props); this.state = { value: undefined, hoverValue: undefined, }; } // 鼠标移出组件时,清空 hoverValue
onMouseLeave = () => { this.setState({ hoverValue: undefined, }); }; onClick = (event, index) => { this.onMouseLeave(); this.setState({ value: index + 1, }); }; onHover = (event, index) => { this.setState({ hoverValue: index + 1, }); }; render() { const { count, className, character, } = this.props; const { value, hoverValue } = this.state; const stars = []; // 根据当前 value 生成所有星星
for (let index = 0; index < count; index += 1) { stars.push( <Star key={index} index={index} count={count} value={hoverValue || value} onClick={this.onClick} onHover={this.onHover} character={character} />, ); } return ( <ul className={className} onMouseLeave={this.onMouseLeave} role="radiogroup"
> {stars} </ul> ); } }
在 Rate 组件中主要记录了实际评分 value 和 hover 状态下的临时评分 hoverValue
当鼠标移动的时候,以 hoverValue 渲染组件
当鼠标移出 Rate 组件时清空 hoverValue,以 value 渲染组件
三、青春版
这种方案以 CSS 为主,HTML 部分相当简单:
摊平了也就是一个简单的 div 包裹了几个 input-radio 元素,这些 radio 都添加了同一个 name
接下来就用神奇的 CSS 一步一步实现评分组件的交互
首先实现选中元素时的效果
/* less */ @color-full: coral; @color-zero: #eee; .rate { margin: 0; padding: 0; // 重置原本的 input-radio 样式 input[name="rate"] { -webkit-appearance: none; border: none; outline: none; cursor: pointer; background: @color-zero; // 点击评分后的效果 &:checked, // 鼠标移入的效果 &:hover { background: @color-full;
} } }
通过 radio 选中时的 :checked 状态,可以修改其点击后的样式
然后再通过相邻元素选择器 ~ ,修改兄弟元素的样式
input[name="rate"] { // 点击评分后的效果 &:checked, &:hover, // 兄弟元素的样式 &:checked ~ input[name="rate"], &:hover ~ input[name="rate"] { background: @color-full;
} }
只是这时候的评分是反向的,没关系,用 flex-flow: row-reverse; 将元素反向排列即可
.rate { display: flex; flex-flow: row-reverse;
}
最后通过 data-set 给 radio 绑定值即可实现取值,不再赘述
参考资料: