chart
Filled radar with glowing stroke
Filled radar with glowing stroke
Загрузка превью…
src/chart/c-chart-24.vue
<!--
Пробел в API: см. c-chart-23.vue / docs/PORTING.md §19/§27 — у vue-chrts
нет ни одного полярного графика. Минимальная самостоятельная 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 = [
{ metric: "Speed", value: 88 },
{ metric: "Reliability", value: 94 },
{ metric: "Scalability", value: 72 },
{ metric: "Cost", value: 68 },
{ metric: "Security", value: 82 },
{ metric: "DX", value: 78 },
]
const chartConfig = {
value: { label: "Score", color: "var(--chart-2)" },
} 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.value / MAX)
return `${p.x},${p.y}`
})
.join(" ")
)
const dataPoints = computed(() => chartData.map((d, i) => pointAt(i, chartData.length, d.value / MAX)))
const labels = computed(() =>
chartData.map((d, i) => ({ ...pointAt(i, chartData.length, 1.18), label: d.metric }))
)
</script>
<template>
<Card class="w-full max-w-xs">
<CardHeader class="items-center pb-0">
<CardTitle>Infrastructure Score</CardTitle>
<CardDescription>Platform performance metrics</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="chart24-fill" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="var(--color-value)" stop-opacity="0.45" />
<stop offset="100%" stop-color="var(--color-value)" stop-opacity="0.1" />
</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(#chart24-fill)" stroke="var(--color-value)" stroke-width="2.5" />
<circle
v-for="(p, i) in dataPoints"
:key="i"
:cx="p.x"
:cy="p.y"
r="4"
fill="var(--background)"
stroke="var(--color-value)"
stroke-width="2.5"
/>
<text
v-for="(l, i) in labels"
:key="i"
:x="l.x"
:y="l.y"
text-anchor="middle"
dominant-baseline="middle"
font-size="11"
fill="var(--muted-foreground)"
>{{ l.label }}</text>
</svg>
</ChartContainer>
</CardContent>
</Card>
</template>