UNPKG

react-native-chart-kit

Version:

Beautiful React Native charts for dashboards, reports, and data-rich mobile apps.

105 lines (104 loc) 3.66 kB
const fullCircle = Math.PI * 2; const defaultStartAngle = -Math.PI / 2; const clamp = (value, min, max) => { if (max < min) { return min; } return Math.min(Math.max(value, min), max); }; const formatNumber = (value) => Number.isInteger(value) ? String(value) : Number(value.toFixed(4)).toString(); const polarToCartesian = ({ angle, centerX, centerY, radius }) => ({ x: centerX + radius * Math.cos(angle), y: centerY + radius * Math.sin(angle) }); const moveTo = (point) => `M ${formatNumber(point.x)} ${formatNumber(point.y)}`; const arcTo = ({ end, largeArc, radius, sweep }) => `A ${formatNumber(radius)} ${formatNumber(radius)} 0 ${largeArc} ${sweep} ${formatNumber(end.x)} ${formatNumber(end.y)}`; const buildArcStrokePath = ({ centerX, centerY, endAngle, radius, startAngle }) => { const span = Math.max(0, endAngle - startAngle); if (radius <= 0 || span <= 0) { return ""; } const isFullCircle = span >= fullCircle - 0.0001; const start = polarToCartesian({ angle: startAngle, centerX, centerY, radius }); const mid = polarToCartesian({ angle: startAngle + Math.PI, centerX, centerY, radius }); const end = polarToCartesian({ angle: isFullCircle ? startAngle + fullCircle : endAngle, centerX, centerY, radius }); if (isFullCircle) { return [ moveTo(start), arcTo({ end: mid, largeArc: 1, radius, sweep: 1 }), arcTo({ end: start, largeArc: 1, radius, sweep: 1 }) ].join(" "); } return [ moveTo(start), arcTo({ end, largeArc: span > Math.PI ? 1 : 0, radius, sweep: 1 }) ].join(" "); }; export const buildProgressRings = ({ rings, centerX, centerY, maxRadius, strokeWidth, ringGap = 8, startAngle = defaultStartAngle }) => { const safeMaxRadius = Number.isFinite(maxRadius) && maxRadius > 0 ? maxRadius : 0; const safeStrokeWidth = Number.isFinite(strokeWidth) && strokeWidth > 0 ? strokeWidth : 0; const safeRingGap = Number.isFinite(ringGap) && ringGap > 0 ? ringGap : 0; const safeStartAngle = Number.isFinite(startAngle) ? startAngle : defaultStartAngle; return rings.map((ring, index) => { const radius = Math.max(0, safeMaxRadius - index * (safeStrokeWidth + safeRingGap)); const value = ring.defined ? ring.value : null; const clampedValue = value !== null && Number.isFinite(value) ? clamp(value, 0, 1) : 0; const endAngle = safeStartAngle + fullCircle * clampedValue; const backgroundPath = buildArcStrokePath({ centerX, centerY, endAngle: safeStartAngle + fullCircle, radius, startAngle: safeStartAngle }); const path = buildArcStrokePath({ centerX, centerY, endAngle, radius, startAngle: safeStartAngle }); const defined = ring.defined && safeStrokeWidth > 0 && radius > 0 && path.length > 0; const model = { index: ring.index, value, clampedValue, defined, radius, strokeWidth: safeStrokeWidth, startAngle: safeStartAngle, endAngle, backgroundPath, path }; if (ring.label !== undefined) { model.label = ring.label; } if (ring.color !== undefined) { model.color = ring.color; } return model; }); };