feat(source): ContribPie material contribution pie chart component
This commit is contained in:
parent
561c5158f1
commit
1a4b752a01
|
|
@ -0,0 +1,59 @@
|
|||
export interface PieItem {
|
||||
name: string
|
||||
rate: number
|
||||
isSrc: boolean
|
||||
}
|
||||
|
||||
interface Props {
|
||||
items: PieItem[]
|
||||
colors?: string[]
|
||||
}
|
||||
|
||||
const DEFAULT_COLORS = ['#C0392B', '#E67E22', '#E0A100', '#159A86', '#2E7FC1', '#6FB1E0', '#9AA7B4']
|
||||
|
||||
const R = 74
|
||||
const CX = 90
|
||||
const CY = 90
|
||||
|
||||
function arc(cx: number, cy: number, r: number, a0: number, a1: number) {
|
||||
const p = (a: number) => [cx + r * Math.cos((a - 90) * Math.PI / 180), cy + r * Math.sin((a - 90) * Math.PI / 180)]
|
||||
const [x0, y0] = p(a0)
|
||||
const [x1, y1] = p(a1)
|
||||
const large = a1 - a0 > 180 ? 1 : 0
|
||||
return `M ${cx} ${cy} L ${x0} ${y0} A ${r} ${r} 0 ${large} 1 ${x1} ${y1} Z`
|
||||
}
|
||||
|
||||
export default function ContribPie({ items, colors = DEFAULT_COLORS }: Props) {
|
||||
const total = items.reduce((s, it) => s + it.rate, 0)
|
||||
if (!items.length || total <= 0) {
|
||||
return <div className="muted" style={{ padding: 20 }}>该污染物无材料释放</div>
|
||||
}
|
||||
const color = (i: number) => colors[Math.min(i, colors.length - 1)]
|
||||
let acc = 0
|
||||
const slices = items.map((it, i) => {
|
||||
const a0 = acc / total * 360
|
||||
acc += it.rate
|
||||
const a1 = acc / total * 360
|
||||
return { d: arc(CX, CY, R, a0, a1), c: color(i), pct: it.rate / total * 100 }
|
||||
})
|
||||
return (
|
||||
<div className="pie-wrap">
|
||||
<svg className="pie-svg" width={180} height={180} viewBox="0 0 180 180">
|
||||
{slices.map((s, i) => (
|
||||
<path key={i} d={s.d} fill={s.c} stroke="#fcfcfb" strokeWidth={2}>
|
||||
<title>{items[i].name}:{(items[i].rate * 100).toFixed(1)}%</title>
|
||||
</path>
|
||||
))}
|
||||
</svg>
|
||||
<div className="pie-legend">
|
||||
{items.map((it, i) => (
|
||||
<div className="pie-lg" key={it.name + i}>
|
||||
<i style={{ background: color(i) }} />
|
||||
<span className="pie-lg-nm">{it.name}{it.isSrc && <b className="pie-src"> (污染源)</b>}</span>
|
||||
<span className="pie-lg-v">{(it.rate * 100).toFixed(1)}%</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -255,3 +255,13 @@
|
|||
.rbc-lg{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--muted,#8a8f98);}
|
||||
.rbc-lg i{width:11px;height:11px;border-radius:3px;display:inline-block;}
|
||||
.rbc-lg-dash{width:14px;height:0;border-top:1px dashed #C0392B;border-radius:0;}
|
||||
|
||||
/* 材料贡献饼图 */
|
||||
.pie-wrap{display:flex;gap:22px;align-items:center;flex-wrap:wrap;padding:6px 2px 2px;}
|
||||
.pie-svg{flex-shrink:0;}
|
||||
.pie-legend{display:flex;flex-direction:column;gap:8px;min-width:200px;flex:1;}
|
||||
.pie-lg{display:flex;align-items:center;gap:9px;font-size:13px;}
|
||||
.pie-lg i{width:12px;height:12px;border-radius:3px;flex-shrink:0;}
|
||||
.pie-lg-nm{flex:1;color:var(--ink,#1f2328);}
|
||||
.pie-src{color:#C0392B;font-weight:700;}
|
||||
.pie-lg-v{font-weight:700;color:var(--ink,#1f2328);}
|
||||
|
|
|
|||
Loading…
Reference in New Issue