小红书 PK 组件的分析与实现

168 阅读3分钟

PK 组件用于可视化展示两种或多种数据的对比结果,特别适用于需要直观展示竞争或对比分析的场景。

效果展示

小红书 PK 组件 image.png

我们将实现一个类似的 PK 组件

image.png

2. 组件设计

2.1 设计理念

PK 组件的设计遵循简洁、直观的原则,旨在通过颜色和比例直观展示数据对比。组件的设计允许用户通过简单的配置即可展示复杂的数据对比结果。

2.2 样式和颜色

组件使用了 Ant Design 的颜色库来定义不同的颜色,便于区分不同的数据项,增强视觉识别度。

import { green, orange, red, yellow } from "@ant-design/colors"

const dataKeyMapToColors: Record<string, string[]> = {
  'apple': red,
  'orange': orange,
  'mango': green
}

2.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)
}

2.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
}

2.5 组件样式

使用 styled-components 库定义了两个样式组件 PkResultCardWrapperCardItem,这些样式组件提供了灵活的样式定制能力。

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;
      `
      : ''}
`

2.6 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>
  )
}

2.7 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>
  )
}

3. 使用方法

3.1 引入组件

在需要使用 PK 组件的地方,引入 PkCardDemo

import { PkCardDemo } from './PkResultCard'

3.2 渲染组件

在 React 应用中渲染 PkCardDemo 组件。

<PkCardDemo />

3.3 定制和扩展

组件的设计允许用户根据需要定制颜色和样式。用户可以通过修改 dataKeyMapToColors 对象来改变不同数据项的颜色。此外,用户可以通过添加更多的数据项到 pkData 对象中来扩展组件。

4. 总结

PK 组件提供了一个直观的方式来展示不同数据项的对比结果。通过颜色编码和比例显示,用户可以快速理解不同数据项的相对大小。组件的设计允许灵活地添加或修改数据项,使其适用于多种场景。通过简单的配置和扩展,PK 组件可以轻松集成到任何需要数据对比展示的 React 应用中。