PK 组件用于可视化展示两种或多种数据的对比结果,特别适用于需要直观展示竞争或对比分析的场景。
效果展示
小红书 PK 组件
我们将实现一个类似的 PK 组件
2. 组件分析
这个组件的难点主要在于样式的处理。我们可以先拆解一下问题:
- PK组件中的每一项形状处理:
-
- 除了首尾两项,其他项都是一个类似于平行四边形的形状,这可以通过将一个长方形在水平方向进行倾斜来实现。可以使用CSS的 transform: skewX() 函数来处理。
- 可以统一设置圆角,保证整体的一致性。
- 首尾两端的特殊处理:
-
- 首尾两项需要单独设置圆角,以确保它们的外观与其他项有所不同。
- 文字的处理:
-
- 文字需要保持正向显示,因此可以对文字也应用
transform: skewX()
,但方向相反,角度相同,用来抵消项的倾斜效果。
- 文字需要保持正向显示,因此可以对文字也应用
- 每一项的宽度比例:
-
- 通过计算每个项的宽度比例(
x/sum
),将其转换成百分比形式,并使用flex-basis: ${props => props.$width}
来设置每一项的宽度。
- 通过计算每个项的宽度比例(
这样拆解之后,问题就变得容易解决了,可以开始编写代码了。一些状态的维护相对简单,就不详细赘述了,具体可以参考代码实现。
3. 具体实现
3.1 组件样式
这是基于组件分析的具体样式实现,可以重点关注一下
import styled from "styled-components"
const PkResultCardWrapper = styled.div<{
len?: number
}>`
width: 800px;
display: flex;
justify-content: space-between;
gap: 8px;
flex-wrap: nowrap;
text-align: center;
font-weight: 500;
${(props) =>
props.len === 1
? `
&>div:first-child{
border-radius: 10px 20px !important;
}
`
: `
&>div:first-child{
border-radius: 10px 4px 8px 20px !important;
}
&>div:last-child{
border-radius: 6px 20px 10px 6px !important;
}
`}
`
const CardItem = styled.div<{
colors?: string[]
$width?: string
}>`
height: 32px;
line-height: 30px;
flex-basis: ${props => props.$width};
border-radius: 6px !important;
transform: skewX(-15deg);
span {
display: inline-block;
transform: skewX(15deg);
}
${(props) =>
props.colors
? `
color: ${props.colors?.[5]} !important;
border: 1px solid ${props.colors?.[2]} !important;
background: ${props.colors?.[0]} !important;
`
: ''}
`
3.2 颜色
组件使用了 Ant Design 的颜色库来定义不同的颜色,便于区分不同的数据项,增强视觉识别度。
import { green, orange, red, yellow } from "@ant-design/colors"
const dataKeyMapToColors: Record<string, string[]> = {
'apple': red,
'orange': orange,
'mango': green
}
3.3 数据结构和计算
组件定义了一个 pkData
对象和一个 getSum
函数,用于存储和计算数据项的总和。这种设计使得组件可以轻松地扩展到更多的数据项。
export const pkData = {
'apple': 5,
'orange': 2
}
const getSum = (data: Record<string, number>) => {
return Object.values(data).reduce((accumulator: number, currentValue: any) => accumulator + Number(currentValue || 0), 0)
}
3.4 结果选项生成
generatePkResultOptions
函数是组件的核心,它根据传入的数据对象生成一个结果数组,每个结果对象包含数据项的键、计数、文本、颜色和比例。
export const generatePkResultOptions = (data: Record<string, number>) => {
const res = []
const sum = getSum(data)
for (const [key, value] of Object.entries(data)) {
res.push({
key,
count: value,
text: key,
colors: dataKeyMapToColors[key],
ratio: (value / sum)
})
}
return res
}
3.5 PK 结果卡片组件
PkResultCard
是一个函数组件,它接收一个 options
数组作为参数,并渲染每个结果卡片。这个组件的设计允许它在不同的上下文中重用。
type PkResultCardProps = {
options: PkResultCardOptions[]
}
export function PkResultCard({ options }: PkResultCardProps) {
return (
<PkResultCardWrapper len={options.length} >
{options?.map(option => {
const ratio = (Number(option.ratio) * 100).toFixed(2) + '%'
return (
<CardItem key={option.key} colors={option.colors} $width={ratio}>
{<span>{`${option.text}: ${option.count} - ${ratio}`}</span>}
</CardItem>
)
})}
</PkResultCardWrapper>
)
}
3.6 PK 卡片演示组件
PkCardDemo
是一个演示组件,它使用 React 的 useState
钩子来管理不同数据项的计数,并使用 useMemo
钩子来生成结果选项。这个组件提供了一个交互式的界面,允许用户动态地增加数据项的计数。
import React, { useMemo } from 'react'
import { generatePkResultOptions } from './data'
import { PkResultCard } from './PkResultCard'
import { Button, Space } from 'antd'
export function PkCardDemo() {
const [apple, setApple] = React.useState(1)
const [orange, setOrange] = React.useState(1)
const [mango, setMango] = React.useState(1)
const options = useMemo(() => generatePkResultOptions({ apple, orange, mango }), [apple, orange, mango])
return (
<Space direction='vertical'>
<Space size={8}>
<Button onClick={() => setApple(apple + 1)}>Apple + 1</Button>
<Button onClick={() => setOrange(orange + 1)}>Orange + 1</Button>
<Button onClick={() => setMango(mango + 1)}>Mango + 1</Button>
</Space>
<PkResultCard options={options} />
</Space>
)
}
4. 使用方法
4.1 引入组件
在需要使用 PK 组件的地方,引入 PkCardDemo
。
import { PkCardDemo } from './PkResultCard'
4.2 渲染组件
在 React 应用中渲染 PkCardDemo
组件。
<PkCardDemo />
4.3 定制和扩展
组件的设计允许用户根据需要定制颜色和样式。用户可以通过修改 dataKeyMapToColors
对象来改变不同数据项的颜色。此外,用户可以通过添加更多的数据项到 pkData
对象中来扩展组件。
5. 总结
PK 组件提供了一个直观的方式来展示不同数据项的对比结果。通过颜色编码和比例显示,用户可以快速理解不同数据项的相对大小。组件的设计允许灵活地添加或修改数据项,使其适用于多种场景。通过简单的配置和扩展,PK 组件可以轻松集成到任何需要数据对比展示的 React 应用中。