评分组件 Rate 的别样解法
各种偏门捷径实现如图所示的评分组件
一、低配版
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 绑定值即可实现取值,不再赘述
参考资料: