Circular Diagram
Diagrama radial SVG com segmentos coloridos por categoria. Suporta variacoes em donut e pie, com legendas interativas e tooltips.
Uso
<div class="circular-diagram">
<h3 class="circular-diagram__title">Distribuicao por Categoria</h3>
<div class="circular-diagram__svg-wrapper">
<svg class="circular-diagram__svg" viewBox="0 0 200 200">
<path class="circular-diagram__segment" d="M100,10 A90,90 0 0,1 190,100" fill="#4caf50" />
<path class="circular-diagram__segment" d="M190,100 A90,90 0 0,1 100,190" fill="#2196f3" />
<path class="circular-diagram__segment" d="M100,190 A90,90 0 0,1 10,100" fill="#ff9800" />
<path class="circular-diagram__segment" d="M10,100 A90,90 0 0,1 100,10" fill="#f44336" />
</svg>
</div>
<div class="circular-diagram__legend">
<div class="circular-diagram__legend-item">
<span class="circular-diagram__legend-dot" style="background: #4caf50"></span>
<span class="circular-diagram__legend-label">Concluido</span>
<span class="circular-diagram__legend-value">42%</span>
</div>
</div>
</div>Exemplos
Anatomia
| Classe | Elemento | Descricao |
|---|---|---|
.circular-diagram | div | Container principal do componente |
__title | h3 | Titulo descritivo do diagrama |
__svg-wrapper | div | Wrapper que centraliza e dimensiona o SVG |
__svg | svg | Elemento SVG raiz com viewBox |
__segment | path | Arco representando uma categoria |
__label | text | Rotulo posicionado sobre ou proximo ao segmento |
__value | text | Valor numerico ou percentual no segmento |
__center | g | Grupo SVG centralizado (visivel no modo donut) |
__center-text | text | Texto exibido no centro do donut (ex.: total) |
__tooltip | div | Caixa de tooltip com detalhes da categoria |
__tooltip--visible | div | Variante visivel do tooltip (hover/focus) |
__legend | div | Container da legenda abaixo do diagrama |
__legend-item | div | Linha individual na legenda |
__legend-dot | span | Circulo colorido correspondente ao segmento |
__legend-label | span | Nome da categoria na legenda |
__legend-value | span | Valor ou percentual na legenda |
__empty | div | Mensagem exibida quando nao ha dados |
SVG Internals
O diagrama utiliza funcoes auxiliares para calcular os arcos SVG:
polarToCartesian
Converte coordenadas polares (angulo + raio) em coordenadas cartesianas (x, y) para posicionar pontos no SVG.
function polarToCartesian(cx, cy, radius, angleDeg) {
const angleRad = (angleDeg - 90) * Math.PI / 180;
return {
x: cx + radius * Math.cos(angleRad),
y: cy + radius * Math.sin(angleRad),
};
}describeArc
Gera o atributo d de um <path> SVG que desenha um arco entre dois angulos.
function describeArc(cx, cy, radius, startAngle, endAngle) {
const start = polarToCartesian(cx, cy, radius, endAngle);
const end = polarToCartesian(cx, cy, radius, startAngle);
const largeArc = endAngle - startAngle <= 180 ? '0' : '1';
return [
'M', start.x, start.y,
'A', radius, radius, 0, largeArc, 0, end.x, end.y,
].join(' ');
}Arcos (arcs)
Cada segmento e construido percorrendo o array de dados e acumulando angulos proporcionais ao valor de cada categoria:
let currentAngle = 0;
const total = data.reduce((sum, d) => sum + d.valor, 0);
const arcs = data.map((d) => {
const angle = (d.valor / total) * 360;
const arc = describeArc(100, 100, 90, currentAngle, currentAngle + angle);
currentAngle += angle;
return { ...d, path: arc };
});Donut vs Pie
O componente suporta dois modos de exibicao controlados pelo innerRadius:
| Modo | innerRadius | Descricao |
|---|---|---|
| Pie | 0 | Grafico de pizza preenchido por completo |
| Donut | 0.6 | Anel com espaco vazio no centro |
Modo Pie (innerRadius: 0)
<div class="circular-diagram" data-inner-radius="0">
<!-- Segmentos preenchem do centro ate a borda -->
</div>No modo pie, cada segmento e desenhado como um setor completo partindo do centro do SVG.
Modo Donut (innerRadius: 0.6)
<div class="circular-diagram" data-inner-radius="0.6">
<svg class="circular-diagram__svg" viewBox="0 0 200 200">
<!-- Segmentos formam um anel -->
<g class="circular-diagram__center">
<text class="circular-diagram__center-text">Total: 1.250</text>
</g>
</svg>
</div>No modo donut, o innerRadius define a proporcao do raio interno em relacao ao raio externo. O valor 0.6 significa que o buraco central ocupa 60% do raio. O grupo __center e __center-text ficam visiveis para exibir informacoes resumidas.
Propriedades
| Propriedade | Tipo | Default | Descrição |
|---|
Codigo
HTML estatico
<div class="circular-diagram">
<h3 class="circular-diagram__title">Distribuicao por Tipo</h3>
<div class="circular-diagram__svg-wrapper">
<svg class="circular-diagram__svg" viewBox="0 0 200 200">
<path class="circular-diagram__segment"
d="M100,10 A90,90 0 0,1 190,100"
fill="#4caf50"
data-label="Concluido"
data-value="35" />
<path class="circular-diagram__segment"
d="M190,100 A90,90 0 0,1 100,190"
fill="#2196f3"
data-label="Em andamento"
data-value="28" />
<path class="circular-diagram__segment"
d="M100,190 A90,90 0 1,1 100,10"
fill="#ff9800"
data-label="Pendente"
data-value="37" />
<g class="circular-diagram__center">
<text class="circular-diagram__center-text" x="100" y="100"
text-anchor="middle" dominant-baseline="central">
100
</text>
</g>
</svg>
</div>
<div class="circular-diagram__tooltip">
<strong>Concluido</strong>: 35 (35%)
</div>
<div class="circular-diagram__legend">
<div class="circular-diagram__legend-item">
<span class="circular-diagram__legend-dot" style="background: #4caf50"></span>
<span class="circular-diagram__legend-label">Concluido</span>
<span class="circular-diagram__legend-value">35</span>
</div>
<div class="circular-diagram__legend-item">
<span class="circular-diagram__legend-dot" style="background: #2196f3"></span>
<span class="circular-diagram__legend-label">Em andamento</span>
<span class="circular-diagram__legend-value">28</span>
</div>
<div class="circular-diagram__legend-item">
<span class="circular-diagram__legend-dot" style="background: #ff9800"></span>
<span class="circular-diagram__legend-label">Pendente</span>
<span class="circular-diagram__legend-value">37</span>
</div>
</div>
</div>Com DataLoader
<div class="circular-diagram"
data-source="/api/distribuicao-tipos.json"
data-inner-radius="0.6">
<h3 class="circular-diagram__title">Distribuicao por Tipo</h3>
<div class="circular-diagram__svg-wrapper">
<svg class="circular-diagram__svg" viewBox="0 0 200 200" data-bind="segments">
<!-- Segmentos renderizados automaticamente -->
</svg>
</div>
<div class="circular-diagram__legend" data-bind="legend">
<!-- Legenda renderizada automaticamente -->
</div>
</div>Auto-init
<div class="circular-diagram" data-auto-init="circular-diagram"
data-source="/api/distribuicao-tipos.json"
data-inner-radius="0.6">
</div>Com data-auto-init, o componente renderiza o SVG, segmentos, legenda e tooltips automaticamente a partir da fonte de dados.
Estado vazio
<div class="circular-diagram">
<h3 class="circular-diagram__title">Distribuicao por Tipo</h3>
<div class="circular-diagram__empty">
Nenhum dado disponivel para exibicao.
</div>
</div>Fonte de Dados
O componente espera um JSON com um array de objetos contendo as seguintes propriedades:
[
{
"label": "Concluido",
"valor": 35,
"color": "#4caf50",
"descricao": "Processos finalizados com parecer"
},
{
"label": "Em andamento",
"valor": 28,
"color": "#2196f3",
"descricao": "Processos em analise ativa"
},
{
"label": "Pendente",
"valor": 37,
"color": "#ff9800",
"descricao": "Processos aguardando distribuicao"
}
]| Propriedade | Tipo | Descricao |
|---|---|---|
label | string | Nome da categoria exibido na legenda e tooltip |
valor | number | Valor numerico que define o tamanho do segmento |
color | string | Cor hexadecimal do segmento e do dot da legenda |
descricao | string | Texto descritivo exibido no tooltip ao passar o mouse |
Dark Mode
O componente responde automaticamente ao dark mode via [data-theme="dark"] ou prefers-color-scheme: dark.
- O fundo do wrapper e da legenda adapta-se ao tema escuro.
- As cores dos segmentos mantem saturacao adequada para contraste no fundo escuro.
- O texto central, rotulos e valores usam cor clara (
--color-text-dark). - O tooltip usa fundo escuro com borda sutil e sombra reduzida.
- Os dots da legenda mantem as cores originais dos segmentos.
/* Exemplo de customizacao no dark mode */
[data-theme="dark"] .circular-diagram__svg-wrapper {
background: var(--color-surface-dark);
}
[data-theme="dark"] .circular-diagram__tooltip {
background: var(--color-surface-elevated-dark);
color: var(--color-text-dark);
}
[data-theme="dark"] .circular-diagram__center-text {
fill: var(--color-text-dark);
}