9.7k

图表

上一页下一页

精美的图表。使用 Unovis 构建。只需复制粘贴到你的应用程序中即可。

柱状图 - 交互式

显示过去 3 个月的访问者总数

隆重推出 图表 (Charts)。这是一套你可以复制粘贴到应用中的图表组件集合。

图表的设计旨在开箱即用,效果出众。它们能与其他组件良好协作,并支持全面自定义以适配你的项目。

浏览图表库.

组件

我们底层使用了 Unovis

我们在设计 chart 组件时,充分考虑了组合性。 你可以使用 Unovis 组件构建图表,并仅在需要的地方引入自定义组件(例如 ChartTooltip

<script setup lang="ts">
import { VisGroupedBar, VisXYContainer } from '@unovis/vue'
import { ChartContainer, ChartTooltipContent } from '@/components/ui/chart'
</script>

<template>
  <ChartContainer :config="chartConfig">
    <VisXYContainer :data="data">
      <VisGroupedBar :x="(d) => d.month" :y="(d) => d.value" />
      <ChartTooltip :template="componentToString(chartConfig, ChartTooltipContent)" />
    </VisXYContainer>
  </ChartContainer>
</template>

我们不对 Unovis 进行封装,这意味着你不会被锁死在特定的抽象层中。当 Unovis 发布新版本时,你可以按照官方的升级路径更新你的图表。

组件完全属于你.

安装

pnpm dlx shadcn-vue@latest add chart

使用方法

<script setup lang="ts">
import type { ChartConfig } from '@/components/ui/chart'
import { VisGroupedBar, VisXYContainer } from '@unovis/vue'
import {
  ChartContainer,
  ChartCrosshair,
  ChartTooltip,
  ChartTooltipContent,
  componentToString,
} from '@/components/ui/chart'

const chartData = [
  { date: new Date("2024-01-01"), desktop: 186, mobile: 80 },
  { date: new Date("2024-02-01"), desktop: 305, mobile: 200 },
  { date: new Date("2024-03-01"), desktop: 237, mobile: 120 },
];
type Data = (typeof chartData)[number]

const chartConfig = {
  desktop: {
    label: "Desktop",
    color: "var(--chart-1)",
  },
  mobile: {
    label: "Mobile",
    color: "var(--chart-2)",
  },
} satisfies ChartConfig
</script>
<template>
  <ChartContainer :config="chartConfig" class="min-h-[400px] w-full">
    <VisXYContainer :data="chartData">
      <VisGroupedBar
        :x="(d: Data) => d.date"
        :y="[(d: Data) => d.desktop, (d: Data) => d.mobile]"
        :color="[chartConfig.desktop.color, chartConfig.mobile.color]"
      />
      <ChartTooltip />
      <ChartCrosshair
        :template="
          componentToString(chartConfig, ChartTooltipContent, {
            labelFormatter(d) {
              return new Date(d).toLocaleDateString('en-US', {
                month: 'long',
              });
            },
          })
        "
        :color="[chartConfig.desktop.color, chartConfig.mobile.color]"
      />
    </VisXYContainer>
  </ChartContainer>
</template>

创建你的第一个图表

让我们来构建你的第一个图表。我们将构建一个柱状图,并添加网格、坐标轴、提示信息和图例。

首先定义你的数据

以下数据代表了每个月的桌面端和移动端用户数量。

const chartData = [
  { month: 'January', desktop: 186, mobile: 80 },
  { month: 'February', desktop: 305, mobile: 200 },
  { month: 'March', desktop: 237, mobile: 120 },
  { month: 'April', desktop: 73, mobile: 190 },
  { month: 'May', desktop: 209, mobile: 130 },
  { month: 'June', desktop: 214, mobile: 140 },
]

定义你的图表配置

图表配置用于存储图表的配置项。你可以在这里设置人类可读的字符串,例如标签、图标和用于主题化的颜色令牌。

import type { ChartConfig } from '@/components/ui/chart'

const chartConfig = {
  desktop: {
    label: 'Desktop',
    color: 'var(--chart-1)',
  },
  mobile: {
    label: 'Mobile',
    color: 'var(--chart-2)',
  },
} satisfies ChartConfig

构建你的图表

现在,你可以使用 Unovis 组件来构建你的图表了。

components/ExampleChart.vue
<script setup lang="ts">
import type { ChartConfig } from '@/components/ui/chart'
import { VisGroupedBar, VisXYContainer } from '@unovis/vue'
import { ChartContainer } from '@/components/ui/chart'

const chartData = [
  { date: new Date('2024-01-01'), desktop: 186, mobile: 80 },
  { date: new Date('2024-02-01'), desktop: 305, mobile: 200 },
  { date: new Date('2024-03-01'), desktop: 237, mobile: 120 },
  { date: new Date('2024-04-01'), desktop: 73, mobile: 190 },
  { date: new Date('2024-05-01'), desktop: 209, mobile: 130 },
  { date: new Date('2024-06-01'), desktop: 214, mobile: 140 },
]

type Data = typeof chartData[number]

const chartConfig = {
  desktop: {
    label: 'Desktop',
    color: '#2563eb',
  },
  mobile: {
    label: 'Mobile',
    color: '#60a5fa',
  },
} satisfies ChartConfig
</script>

<template>
  <ChartContainer :config="chartConfig" class="min-h-[200px] w-full">
    <VisXYContainer :data="chartData">
      <VisGroupedBar
        :x="(d: Data) => d.date"
        :y="[(d: Data) => d.desktop, (d: Data) => d.mobile]"
        :color="[chartConfig.desktop.color, chartConfig.mobile.color]"
        :rounded-corners="4"
        bar-padding="0.1"
        group-padding="0"
      />
    </VisXYContainer>
  </ChartContainer>
</template>
<script setup lang="ts">
import type { ChartConfig } from '@/components/ui/chart'
import { VisGroupedBar, VisXYContainer } from '@unovis/vue'
import { ChartContainer } from '@/components/ui/chart'

const chartData = [
  { date: new Date('2024-01-01'), desktop: 186, mobile: 80 },
  { date: new Date('2024-02-01'), desktop: 305, mobile: 200 },
  { date: new Date('2024-03-01'), desktop: 237, mobile: 120 },
  { date: new Date('2024-04-01'), desktop: 73, mobile: 190 },
  { date: new Date('2024-05-01'), desktop: 209, mobile: 130 },
  { date: new Date('2024-06-01'), desktop: 214, mobile: 140 },
]

type Data = typeof chartData[number]

const chartConfig = {
  desktop: {
    label: 'Desktop',
    color: '#2563eb',
  },
  mobile: {
    label: 'Mobile',
    color: '#60a5fa',
  },
} satisfies ChartConfig
</script>

<template>
  <ChartContainer :config="chartConfig" class="min-h-[200px] w-full">
    <VisXYContainer :data="chartData">
      <VisGroupedBar
        :x="(d: Data) => d.date"
        :y="[(d: Data) => d.desktop, (d: Data) => d.mobile]"
        :color="[chartConfig.desktop.color, chartConfig.mobile.color]"
        :rounded-corners="4"
        bar-padding="0.1"
        group-padding="0"
      />
    </VisXYContainer>
  </ChartContainer>
</template>

添加坐标轴

要向图表添加坐标轴,我们使用 VisAxis 组件。

导入 VisAxis 组件

import { VisAxis, VisGroupedBar, VisXYContainer } from '@unovis/vue'

VisAxis 组件添加到你的图表中

<template>
  <VisAxis
    type="x"
    :x="(d: Data) => d.date"
    :tick-line="false"
    :domain-line="false"
    :grid-line="false"
    :tick-format="(d: number) => {
      const date = new Date(d)
      return date.toLocaleDateString('en-US', {
        month: 'short',
      })
    }"
    :tick-values="chartData.map(d => d.date)"
  />
  <VisAxis
    type="y"
    :tick-format="(d: number) => ''"
    :tick-line="false"
    :domain-line="false"
    :grid-line="true"
  />
</template>
<script setup lang="ts">
import type { ChartConfig } from '@/components/ui/chart'
import { VisAxis, VisGroupedBar, VisXYContainer } from '@unovis/vue'
import { ChartContainer } from '@/components/ui/chart'

const chartData = [
  { date: new Date('2024-01-01'), desktop: 186, mobile: 80 },
  { date: new Date('2024-02-01'), desktop: 305, mobile: 200 },
  { date: new Date('2024-03-01'), desktop: 237, mobile: 120 },
  { date: new Date('2024-04-01'), desktop: 73, mobile: 190 },
  { date: new Date('2024-05-01'), desktop: 209, mobile: 130 },
  { date: new Date('2024-06-01'), desktop: 214, mobile: 140 },
]

type Data = typeof chartData[number]

const chartConfig = {
  desktop: {
    label: 'Desktop',
    color: '#2563eb',
  },
  mobile: {
    label: 'Mobile',
    color: '#60a5fa',
  },
} satisfies ChartConfig
</script>

<template>
  <ChartContainer :config="chartConfig" class="min-h-[200px] w-full">
    <VisXYContainer :data="chartData">
      <VisGroupedBar
        :x="(d: Data) => d.date"
        :y="[(d: Data) => d.desktop, (d: Data) => d.mobile]"
        :color="[chartConfig.desktop.color, chartConfig.mobile.color]"
        :rounded-corners="4"
        bar-padding="0.1"
        group-padding="0"
      />
      <VisAxis
        type="x"
        :x="(d: Data) => d.date"
        :tick-line="false"
        :domain-line="false"
        :grid-line="false"
        :tick-format="(d: number) => {
          const date = new Date(d)
          return date.toLocaleDateString('en-US', {
            month: 'short',
          })
        }"
        :tick-values="chartData.map(d => d.date)"
      />
      <VisAxis
        type="y"
        :tick-format="(d: number) => ''"
        :tick-line="false"
        :domain-line="false"
        :grid-line="true"
      />
    </VisXYContainer>
  </ChartContainer>
</template>

添加提示信息 (Tooltip)

要添加提示信息,我们将使用来自 chart 的自定义组件 ChartTooltipChartTooltipContent

导入 ChartTooltipChartTooltipContent 组件

import { ChartTooltip, ChartTooltipContent, componentToString } from '@/components/ui/chart'

将这些组件添加到你的图表中

<ChartTooltip />

<ChartCrosshair :template="componentToString(chartConfig, ChartTooltipContent)" />
<script setup lang="ts">
import type { ChartConfig } from '@/components/ui/chart'
import { VisAxis, VisGroupedBar, VisXYContainer } from '@unovis/vue'
import { ChartContainer, ChartCrosshair, ChartTooltip, ChartTooltipContent, componentToString } from '@/components/ui/chart'

const chartData = [
  { date: new Date('2024-01-01'), desktop: 186, mobile: 80 },
  { date: new Date('2024-02-01'), desktop: 305, mobile: 200 },
  { date: new Date('2024-03-01'), desktop: 237, mobile: 120 },
  { date: new Date('2024-04-01'), desktop: 73, mobile: 190 },
  { date: new Date('2024-05-01'), desktop: 209, mobile: 130 },
  { date: new Date('2024-06-01'), desktop: 214, mobile: 140 },
]

type Data = typeof chartData[number]

const chartConfig = {
  desktop: {
    label: 'Desktop',
    color: '#2563eb',
  },
  mobile: {
    label: 'Mobile',
    color: '#60a5fa',
  },
} satisfies ChartConfig
</script>

<template>
  <ChartContainer :config="chartConfig" class="min-h-[200px] w-full">
    <VisXYContainer :data="chartData">
      <VisGroupedBar
        :x="(d: Data) => d.date"
        :y="[(d: Data) => d.desktop, (d: Data) => d.mobile]"
        :color="[chartConfig.desktop.color, chartConfig.mobile.color]"
        :rounded-corners="4"
        bar-padding="0.1"
        group-padding="0"
      />
      <VisAxis
        type="x"
        :x="(d: Data) => d.date"
        :tick-line="false"
        :domain-line="false"
        :grid-line="false"
        :tick-format="(d: number) => {
          const date = new Date(d)
          return date.toLocaleDateString('en-US', {
            month: 'short',
          })
        }"
        :tick-values="chartData.map(d => d.date)"
      />
      <VisAxis
        type="y"
        :tick-format="(d: number) => ''"
        :tick-line="false"
        :domain-line="false"
        :grid-line="true"
      />
      <ChartTooltip />
      <ChartCrosshair
        :template="componentToString(chartConfig, ChartTooltipContent, {
          labelFormatter(d) {
            return new Date(d).toLocaleDateString('en-US', {
              month: 'long',
            })
          },
        })"
        :color="[chartConfig.desktop.color, chartConfig.mobile.color]"
      />
    </VisXYContainer>
  </ChartContainer>
</template>

悬停查看提示信息。很简单,对吧?只需两个组件,我们就得到了精美的提示信息。

添加图例

我们对图例进行同样的操作。我们将使用来自 chartChartLegendChartLegendContent 组件。

导入 ChartLegendContent 组件。

import { ChartLegendContent } from '@/components/ui/chart'

将这些组件添加到你的图表中。

<template>
  <ChartContainer :config="chartConfig" class="min-h-[200px] w-full">
    <VisXYContainer :data="chartData" />
    <ChartLegendContent />
  </ChartContainer>
</template>
<script setup lang="ts">
import type { ChartConfig } from '@/components/ui/chart'
import { VisAxis, VisGroupedBar, VisXYContainer } from '@unovis/vue'
import { ChartContainer, ChartCrosshair, ChartLegendContent, ChartTooltip, ChartTooltipContent, componentToString } from '@/components/ui/chart'

const chartData = [
  { date: new Date('2024-01-01'), desktop: 186, mobile: 80 },
  { date: new Date('2024-02-01'), desktop: 305, mobile: 200 },
  { date: new Date('2024-03-01'), desktop: 237, mobile: 120 },
  { date: new Date('2024-04-01'), desktop: 73, mobile: 190 },
  { date: new Date('2024-05-01'), desktop: 209, mobile: 130 },
  { date: new Date('2024-06-01'), desktop: 214, mobile: 140 },
]

type Data = typeof chartData[number]

const chartConfig = {
  desktop: {
    label: 'Desktop',
    color: '#2563eb',
  },
  mobile: {
    label: 'Mobile',
    color: '#60a5fa',
  },
} satisfies ChartConfig
</script>

<template>
  <ChartContainer :config="chartConfig" class="min-h-[200px] w-full">
    <VisXYContainer :data="chartData">
      <VisGroupedBar
        :x="(d: Data) => d.date"
        :y="[(d: Data) => d.desktop, (d: Data) => d.mobile]"
        :color="[chartConfig.desktop.color, chartConfig.mobile.color]"
        :rounded-corners="4"
        bar-padding="0.1"
        group-padding="0"
      />
      <VisAxis
        type="x"
        :x="(d: Data) => d.date"
        :tick-line="false"
        :domain-line="false"
        :grid-line="false"
        :tick-format="(d: number) => {
          const date = new Date(d)
          return date.toLocaleDateString('en-US', {
            month: 'short',
          })
        }"
        :tick-values="chartData.map(d => d.date)"
      />
      <VisAxis
        type="y"
        :tick-format="(d: number) => ''"
        :tick-line="false"
        :domain-line="false"
        :grid-line="true"
      />
      <ChartTooltip />
      <ChartCrosshair
        :template="componentToString(chartConfig, ChartTooltipContent, {
          labelFormatter(d) {
            return new Date(d).toLocaleDateString('en-US', {
              month: 'long',
            })
          },
        })"
        :color="[chartConfig.desktop.color, chartConfig.mobile.color]"
      />
    </VisXYContainer>

    <ChartLegendContent />
  </ChartContainer>
</template>

完成。你已经构建了第一个图表!接下来做什么?

图表配置

图表配置是定义图表标签、图标和颜色的地方。

它有意与图表数据解耦。

这允许你在不同图表间共享配置和颜色令牌。它也可以独立工作,适用于你的数据或颜色令牌远程存储或采用不同格式的情况。

<script setup lang="ts">
import type { ChartConfig } from '@/components/ui/chart'
import { Monitor } from 'lucide-vue-next'

const chartConfig = {
  desktop: {
    label: 'Desktop',
    icon: Monitor,
    // A color like 'hsl(220, 98%, 61%)' or 'var(--color-name)'
    color: 'var(--chart-1)',
    // OR a theme object with 'light' and 'dark' keys
    theme: {
      light: 'var(--chart-1)',
      dark: 'var(--chart-2)',
    },
  },
} satisfies ChartConfig
</script>

主题化

图表具有内置的主题支持。你可以使用 CSS 变量(推荐)或任何颜色格式的颜色值,如 hex、hsl 或 oklch。

CSS 变量

在你的 CSS 文件中定义颜色

@layer base {
  :root {
    --chart-1: oklch(0.646 0.222 41.116);
    --chart-2: oklch(0.6 0.118 184.704);
  }

  .dark {
    --chart-1: oklch(0.488 0.243 264.376);
    --chart-2: oklch(0.696 0.17 162.48);
  }
}

将颜色添加到你的 chartConfig

const chartConfig = {
  desktop: {
    label: 'Desktop',
    color: 'var(--chart-1)',
  },
  mobile: {
    label: 'Mobile',
    color: 'var(--chart-2)',
  },
} satisfies ChartConfig

hex、hsl 或 oklch

你也可以直接在图表配置中定义颜色。使用你喜欢的颜色格式即可。

const chartConfig = {
  desktop: {
    label: 'Desktop',
    color: '#2563eb',
  },
} satisfies ChartConfig

使用颜色

要在图表中使用主题颜色,请使用 var(--color-KEY) 格式引用它们。

组件

<VisGroupedBar
  :x="(d) => d.month"
  :y="(d) => d.desktop"
  color="var(--color-desktop)"
/>

图表数据

const chartData = [
  { browser: 'chrome', visitors: 275, fill: 'var(--color-chrome)' },
  { browser: 'safari', visitors: 200, fill: 'var(--color-safari)' },
]

提示信息

图表提示信息包含标签、名称、指示器和数值。你可以组合使用它们来自定义你的提示信息。

你可以使用 hideLabelhideIndicator 属性开启/关闭这些功能,并使用 indicator 属性自定义指示器样式。

使用 labelKeynameKey 来为提示信息的标签和名称指定自定义键。

图表库自带 ChartTooltipChartTooltipContent 组件。你可以使用这两个组件向图表添加自定义提示信息。

import { ChartTooltip, ChartTooltipContent } from '@/components/ui/chart'
<template>
  <ChartTooltip />
  <ChartCrosshair
    :template="componentToString(chartConfig, ChartTooltipContent)"
  />
</template>

属性 (Props)

使用以下属性来自定义提示信息。

属性类型描述
labelKeystring用于标签的配置或数据键。
nameKeystring用于名称的配置或数据键。
indicatordot(点)、line(线)或 dashed(虚线)提示信息的指示器样式。
hideLabelboolean是否隐藏标签。
hideIndicatorboolean是否隐藏指示器。

颜色

颜色会自动从图表配置中引用。

自定义

若要为提示信息的标签和名称使用自定义键,请使用 labelKeynameKey 属性。

const chartData = [
  { browser: 'chrome', visitors: 187, fill: 'var(--color-chrome)' },
  { browser: 'safari', visitors: 200, fill: 'var(--color-safari)' },
]

const chartConfig = {
  visitors: {
    label: 'Total Visitors',
  },
  chrome: {
    label: 'Chrome',
    color: 'var(--chart-1)',
  },
  safari: {
    label: 'Safari',
    color: 'var(--chart-2)',
  },
} satisfies ChartConfig
<template>
  <ChartCrosshair
    :template="componentToString(chartConfig, ChartTooltipContent, {
      labelKey: 'visitors',
      nameKey: 'browser',
    })"
  />
</template>

这将使用 Total Visitors 作为标签,并使用 ChromeSafari 作为提示信息的名称。

图例

你可以使用自定义的 <ChartLegendContent> 组件为图表添加图例。

import { ChartLegendContent } from '@/components/ui/chart'
<template>
  <ChartLegendContent />
</template>

颜色

颜色会自动从图表配置中引用。

自定义

若要为图例名称使用自定义键,请使用 nameKey 属性。

const chartData = [
  { browser: 'chrome', visitors: 187, fill: 'var(--color-chrome)' },
  { browser: 'safari', visitors: 200, fill: 'var(--color-safari)' },
]

const chartConfig = {
  chrome: {
    label: 'Chrome',
    color: 'hsl(var(--chart-1))',
  },
  safari: {
    label: 'Safari',
    color: 'hsl(var(--chart-2))',
  },
} satisfies ChartConfig
<template>
  <ChartLegendContent name-key="browser" />
</template>

这将使用 ChromeSafari 作为图例名称。