chart

Radar chart with gradient fill

Radar chart with gradient fill

Загрузка превью…

src/chart/c-chart-23.vue

<!--
  Пробел в API, не просто замена: `vue-chrts` (в отличие от recharts) не
  экспортирует ни `RadarChart`, ни `PolarGrid`/`PolarAngleAxis` вовсе — в
  пакете нет ни одного полярного графика (см. docs/PORTING.md §19/§27,
  полный список компонентов `vue-chrts`: AreaChart/LineChart/BarChart/
  DonutChart/BubbleChart/GanttChart/DagreGraph/DualChart/SankeyChart/Maps).
  Заменить recharts `RadarChart` на "другой рендерер того же типа графика"
  здесь невозможно — такого типа графика в vue-chrts нет вообще.
  Ниже — минимальная самостоятельно нарисованная SVG-паутина (полигон по
  тем же данным/углам), а не попытка сымитировать recharts алгоритм
  тик-в-тик: сетка градуирована на 4 уровня, оси подписаны по `skill`.
  Градиентная заливка и glow-фильтр перенесены (это чистый SVG, не завязан
  на конкретную чарт-библиотеку).
-->
<script setup lang="ts">
import { computed } from "vue"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { ChartContainer, type ChartConfig } from "@/components/ui/chart"

const chartData = [
  { skill: "Frontend", score: 92 },
  { skill: "Backend", score: 78 },
  { skill: "DevOps", score: 68 },
  { skill: "Design", score: 74 },
  { skill: "Testing", score: 85 },
  { skill: "Security", score: 62 },
]

const chartConfig = {
  score: { label: "Proficiency", color: "var(--chart-1)" },
} satisfies ChartConfig

const SIZE = 280
const CENTER = SIZE / 2
const RADIUS = 100
const MAX = 100
const LEVELS = 4

function pointAt(index: number, total: number, fraction: number) {
  const angle = (Math.PI * 2 * index) / total - Math.PI / 2
  return {
    x: CENTER + Math.cos(angle) * RADIUS * fraction,
    y: CENTER + Math.sin(angle) * RADIUS * fraction,
  }
}

const gridPolygons = computed(() =>
  Array.from({ length: LEVELS }, (_, level) => {
    const fraction = (level + 1) / LEVELS
    return chartData
      .map((_, i) => {
        const p = pointAt(i, chartData.length, fraction)
        return `${p.x},${p.y}`
      })
      .join(" ")
  })
)

const spokes = computed(() =>
  chartData.map((_, i) => pointAt(i, chartData.length, 1))
)

const dataPolygon = computed(() =>
  chartData
    .map((d, i) => {
      const p = pointAt(i, chartData.length, d.score / MAX)
      return `${p.x},${p.y}`
    })
    .join(" ")
)

const dataPoints = computed(() =>
  chartData.map((d, i) => pointAt(i, chartData.length, d.score / MAX))
)

const labels = computed(() =>
  chartData.map((d, i) => ({ ...pointAt(i, chartData.length, 1.18), label: d.skill }))
)
</script>

<template>
  <Card class="w-full max-w-xs">
    <CardHeader class="items-center pb-0">
      <CardTitle>Skill Assessment</CardTitle>
      <CardDescription>Team proficiency across key areas</CardDescription>
    </CardHeader>
    <CardContent class="flex-1 pb-0">
      <ChartContainer :config="chartConfig" class="mx-auto aspect-square max-h-[280px]">
        <svg :viewBox="`0 0 ${SIZE} ${SIZE}`" class="h-full w-full">
          <defs>
            <linearGradient id="chart23-fill" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0%" stop-color="var(--color-score)" stop-opacity="0.5" />
              <stop offset="100%" stop-color="var(--color-score)" stop-opacity="0.08" />
            </linearGradient>
          </defs>
          <polygon
            v-for="(poly, i) in gridPolygons"
            :key="i"
            :points="poly"
            fill="none"
            stroke="var(--border)"
            stroke-dasharray="3 3"
          />
          <line
            v-for="(p, i) in spokes"
            :key="i"
            :x1="CENTER"
            :y1="CENTER"
            :x2="p.x"
            :y2="p.y"
            stroke="var(--border)"
            stroke-dasharray="3 3"
          />
          <polygon :points="dataPolygon" fill="url(#chart23-fill)" stroke="var(--color-score)" stroke-width="2" />
          <circle
            v-for="(p, i) in dataPoints"
            :key="i"
            :cx="p.x"
            :cy="p.y"
            r="4"
            fill="var(--color-score)"
          />
          <text
            v-for="(l, i) in labels"
            :key="i"
            :x="l.x"
            :y="l.y"
            text-anchor="middle"
            dominant-baseline="middle"
            font-size="12"
            fill="var(--muted-foreground)"
          >{{ l.label }}</text>
        </svg>
      </ChartContainer>
    </CardContent>
  </Card>
</template>

Установка

npx shadcn-vue@latest add https://revueui.rootapi.dev/r/c-chart-23.json

Зависимости реестра

Источник: порт из ReUI (Keenthemes, MIT)