small visual changes
This commit is contained in:
@@ -25,18 +25,37 @@
|
|||||||
let grandTotal = $derived(Object.values(continentTotals).reduce((a, b) => a + b, 0));
|
let grandTotal = $derived(Object.values(continentTotals).reduce((a, b) => a + b, 0));
|
||||||
let pct = $derived(grandTotal > 0 ? Math.round(total / grandTotal * 100) : 0);
|
let pct = $derived(grandTotal > 0 ? Math.round(total / grandTotal * 100) : 0);
|
||||||
|
|
||||||
let donutStyle = $derived.by(() => {
|
let segments = $derived.by(() => {
|
||||||
if (total === 0) return 'background: #e2e8f0';
|
if (total === 0) return [];
|
||||||
|
const segs = [];
|
||||||
let deg = 0;
|
let deg = 0;
|
||||||
const stops = [];
|
|
||||||
for (const cont of CONTINENTS) {
|
for (const cont of CONTINENTS) {
|
||||||
const angle = counts[cont] / total * 360;
|
const angle = counts[cont] / total * 360;
|
||||||
if (angle > 0) {
|
if (angle > 0) {
|
||||||
stops.push(`${continentColors[cont]} ${deg}deg ${deg + angle}deg`);
|
const startDeg = deg;
|
||||||
|
const endDeg = deg + angle;
|
||||||
|
const midDeg = (startDeg + endDeg) / 2;
|
||||||
|
const rad = (midDeg - 90) * Math.PI / 180;
|
||||||
|
const sr = (startDeg - 90) * Math.PI / 180;
|
||||||
|
const er = (endDeg - 90) * Math.PI / 180;
|
||||||
|
const cx = 90, cy = 90, outerR = 65, innerR = 30;
|
||||||
|
const x1 = cx + outerR * Math.cos(sr);
|
||||||
|
const y1 = cy + outerR * Math.sin(sr);
|
||||||
|
const x2 = cx + outerR * Math.cos(er);
|
||||||
|
const y2 = cy + outerR * Math.sin(er);
|
||||||
|
const x3 = cx + innerR * Math.cos(er);
|
||||||
|
const y3 = cy + innerR * Math.sin(er);
|
||||||
|
const x4 = cx + innerR * Math.cos(sr);
|
||||||
|
const y4 = cy + innerR * Math.sin(sr);
|
||||||
|
const largeArc = angle > 180 ? 1 : 0;
|
||||||
|
const path = `M ${x1} ${y1} A ${outerR} ${outerR} 0 ${largeArc} 1 ${x2} ${y2} L ${x3} ${y3} A ${innerR} ${innerR} 0 ${largeArc} 0 ${x4} ${y4} Z`;
|
||||||
|
const lx = cx + 82 * Math.cos(rad);
|
||||||
|
const ly = cy + 82 * Math.sin(rad);
|
||||||
|
segs.push({ cont, color: continentColors[cont], path, lx, ly, angle });
|
||||||
deg += angle;
|
deg += angle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return `background: conic-gradient(${stops.join(', ')})`;
|
return segs;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -64,9 +83,22 @@
|
|||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
<div class="donut-wrap">
|
<div class="donut-wrap">
|
||||||
<div class="donut" style={donutStyle}>
|
{#if segments.length > 0}
|
||||||
<div class="donut-hole"></div>
|
<svg viewBox="0 0 180 180" class="donut-svg">
|
||||||
</div>
|
{#each segments as seg}
|
||||||
|
<g class="seg-group">
|
||||||
|
<path d={seg.path} fill={seg.color} />
|
||||||
|
<text x={seg.lx} y={seg.ly} text-anchor="middle" dominant-baseline="middle" class="donut-label" style="font-size: {seg.angle < 20 ? 7 : 9}px">{seg.cont}</text>
|
||||||
|
</g>
|
||||||
|
{/each}
|
||||||
|
<circle cx="90" cy="90" r="30" fill="#f8fafc" />
|
||||||
|
</svg>
|
||||||
|
{:else}
|
||||||
|
<svg viewBox="0 0 180 180" class="donut-svg">
|
||||||
|
<circle cx="90" cy="90" r="65" fill="#e2e8f0" />
|
||||||
|
<circle cx="90" cy="90" r="30" fill="#f8fafc" />
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
@@ -177,22 +209,22 @@
|
|||||||
margin: 24px 0;
|
margin: 24px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.donut {
|
.donut-svg {
|
||||||
width: 130px;
|
width: 160px;
|
||||||
height: 130px;
|
height: 160px;
|
||||||
border-radius: 50%;
|
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
||||||
outline: 2px solid #fff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.donut-hole {
|
.donut-label {
|
||||||
width: 60px;
|
fill: #1f2937;
|
||||||
height: 60px;
|
font-weight: 600;
|
||||||
border-radius: 50%;
|
pointer-events: none;
|
||||||
background: #f8fafc;
|
opacity: 0;
|
||||||
|
transition: opacity 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.seg-group:hover .donut-label {
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.disclaimer {
|
.disclaimer {
|
||||||
|
|||||||
@@ -135,7 +135,7 @@
|
|||||||
.datum(d)
|
.datum(d)
|
||||||
.attr('cx', cx)
|
.attr('cx', cx)
|
||||||
.attr('cy', cy)
|
.attr('cy', cy)
|
||||||
.attr('r', 3)
|
.attr('r', 2)
|
||||||
.attr('fill', getSelected().has(effId(d)) ? '#22c55e' : '#ffffff')
|
.attr('fill', getSelected().has(effId(d)) ? '#22c55e' : '#ffffff')
|
||||||
.attr('stroke', '#94a3b8')
|
.attr('stroke', '#94a3b8')
|
||||||
.attr('stroke-width', 0.5);
|
.attr('stroke-width', 0.5);
|
||||||
|
|||||||
Reference in New Issue
Block a user