Rate
是一个评分元件。一方面可以对於评价的数据展示,另一方面可以让人进行对评分的操作。
因为 MUI 目前的版本还没有 Rate 元件,因此我们这边先只参考 Antd 的元件。
count
虽然我们平常常见的评分都是五颗星星,但这边给了这个 count 的参数让我们不被限制於只能五颗星星,我觉得还蛮不错的,这边的预设也是五颗星。
allowHalf
这个参数允许我们选择半颗星星,而且他很酷的是,他允许我们只 hover 一半的星星,到底是怎麽做到的呢?这边来看一下他的 html 结构:
另外,我们这边发现 ant-rate-star-first 这个 div 节点有一个关键的 css 属性,就是 width: 50%;
,如下图所示:
透过上述的结构,我们可以大概猜出实作 hover 一半星星的逻辑,首先我们要准备 active 星星全颗,还有 inactive 星星全颗。
接着,在初始状态,我们先把 width: 50%;
的 active 星星完全重叠在 inactive 星星上面(就是图中的 star-first div),这边是透过 position: absolute;
属性来实作重叠的效果,并且让 active 星星先隐藏,这边隐藏的方式是把 opacity 变成 0。
当我们 hover 在左半边的 star-first div 上面时,就让 active 的星星显现(opacity: 1;
),并且保持 width: 50%;
,这样看起来就是半颗星 active 的效果;当我们 hover 在右半边的 star-second div 上面时,我们就改变被叠在下面的星星的颜色,从灰色变成黄色,也就是从 inactive 变成 active,这样看起来就会变成一颗全星的效果。
disabled
设定了 disabled 为 true 时, Rate 就不能够让使用者操作,变成已读的状态,也就是纯展示的功能。
character
character 属性也是让我蛮惊艳的属性之一,就是他做到可以替换 Rate 字符,言外之意就是你不一定要被他限制住是星星图案,你也可以是爱心,甚至也可以是文字,我觉得这个非常的酷,因为上面我们解析他的半颗 hover 功能是透过改变 color 来实现,感觉传入的属性也是需要能够支援 color 可以被改变,因此这边文件写说限定的型别就是 ReactNode,从范例中也可以看出,他支援 icon 以及文字的传入。
属性 | 说明 | 类型 | 默认值 |
---|---|---|---|
count | star 总数 | number | 5 |
allowHalf | 是否允许半颗星星 | boolean | false |
disabled | 是否能进行交互 | boolean | false |
defaultValue | 预设分数 | number | |
themeColor | 主题颜色 | number | |
size | star 大小 | number | 32 |
character | 自定义字符 | ReactNode , String |
简化来看的话,我们 Rate 整体逻辑架构概念回如下,由一个 <RateWrapper />
的根节点包覆住整个元件,并且也由这个跟节点决定内部元件布局排版,以 Rate 为例,应该是 row 方向的布局,因此我们可以用 flex 来实现。
我们决定 star character 总数的 props 是 count
,因此由下面程序码范例,我们传入的 count
为多少,就能够产生长度为多少的阵列:
<RateWrapper>
{
[...Array(count).keys()].map((itemKey) => (
<Character key={itemKey} />
))
}
</RateWrapper>
接着我们来实现 Character
,我们预设的 Character 是星星 <StarIcon />
,由先前元件分析可知,要做到能够允许选取半颗星星,需要两个元件 <CharacterFirst />
和 <CharacterSecond />
一起来搭配才能实现:
<RateWrapper>
{
[...Array(count).keys()].map((itemKey) => (
<CharacterWrapper key={itemKey}>
<CharacterFirst>{character}</CharacterFirst>
<CharacterSecond>{character}</CharacterSecond>
</CharacterWrapper>
))
}
</RateWrapper>
布局上,为了实现半星选取,<CharacterFirst />
必须要设为 position: absolute;
,如此 <CharacterFirst />
和 <CharacterSecond />
才能够重叠,并且 <CharacterFirst />
的 width 需要设为 50% 来表示半星。
当我们不需要伴星选取的时候,只需要隐藏 <CharacterFirst />
就能够做到了。
const CharacterFirst = styled.div`
position: absolute;
color: ${(props) => (props.$isActive ? props.$starColor : '#F0F0F0')};
width: 50%;
overflow: hidden;
cursor: pointer;
`;
布局完成之後,接着我们要做的是 hover 的时候能够预览选取样式,因此 hover 到哪里就要 active 到哪里,当滑鼠移开的时候,则回覆到原本选取状态
因此我们需要一个 state 用来记录预览状态,另一个 state 则是用来记录实际上的选取状态:
const [innerValue, setInnerValue] = useState(defaultValue);
const [previewValue, setPreviewValue] = useState(innerValue);
当滑鼠 hover 上去的时候,我们呼叫 onMouseOver
事件,若 hover 在 <CharacterFirst />
表示半星,所以要 +0.5
;若 hover 在 <CharacterSecond />
表示全颗星,所以要 +1
。
<CharacterWrapper key={itemKey}>
<CharacterFirst
className="rate__character-first"
$starColor={starColor}
$isActive={itemKey + 0.5 <= previewValue}
onMouseOver={() => handleChangePreviewValue(itemKey + 0.5)}
onMouseLeave={() => handleChangePreviewValue(innerValue)}
onClick={() => handleOnClick(itemKey + 0.5)}
>
{character}
</CharacterFirst>
<CharacterSecond
className="rate__character-second"
$starColor={starColor}
$isActive={itemKey + 1 <= previewValue}
onMouseOver={() => handleChangePreviewValue(itemKey + 1)}
onMouseLeave={() => handleChangePreviewValue(innerValue)}
onClick={() => handleOnClick(itemKey + 1)}
>
{character}
</CharacterSecond>
</CharacterWrapper>
当滑鼠移开的时候,则透过 onMouseLeave
事件来改变 previewValue
,设定回原本该有的值:
const handleChangePreviewValue = (currentValue) => {
if (!isDisabled) {
setPreviewValue(currentValue);
}
};
onClick
事件则是确定选取的时候呼叫,因此要改变 innerValue
,那如果 onClick
的时候我们发现选取值与原本的值一样,则表示他想要取消选择,此时我们将 innerValue
设为 0:
const handleOnClick = (clickedValue) => {
if (isDisabled) return;
setInnerValue((previousValue) => (previousValue === clickedValue ? 0 : clickedValue));
};
那到目前为止,关於 Rate 主要的关键功能就都完成了!透过以上的方法,我们藉由 props 来改变 character
也不会是难事了:
Rate 元件原始码:
Source code
Storybook:
Rate
<<: Day 6 - Search Insert Position
>>: DAY 9 『 CollectionView 』Part2
写在前面 今天加班,所以等等把文章顺过之後再补上,先发文 今天加班,所以等等把文章顺过之後再补上,先...
大家好, 因为工作不太常用到AI/ML, 所以我自身会想要去多看多了解, 才不会脱钩 想当初整整研究...
Redis Request Routing 在Redis Server丛集中所有的操作透过Reque...
1.is 开头的大部分回传值是booling ex:isNaN() https://www.w3sc...
接续昨天的练习:https://ithelp.ithome.com.tw/articles/1026...