'Adding numbering to SVG path shapes in JavaScript using .getBBox() method
I am trying to put numbers inside custom shapes. I am using getBBox() to find center of the shape. It works only on certain shapes, because center point may not be inside a hollow shape.
Demo on Codepen:
https://codepen.io/arindamx01/pen/KKyLNrj?editors=1010
Here is my JavaScript code:
let colorGroup = ['#700843', '#E95C87', '#AED9C5', '#FDFCFD', '#F8AF3F'];
colorGroup.forEach( function(e, i) {
console.log(e, i);
$(`path[data-color-group = "${e}"]`).each(function(){
let pathBBox = $(this)[0].getBBox();
console.log(pathBBox, $(this)[0])
let getPathData = $(this).attr("data-index");
var svgNS = "http://www.w3.org/2000/svg";
var newText = document.createElementNS(svgNS,"text");
newText.setAttributeNS(null,"x", pathBBox.x + pathBBox.width/2);
newText.setAttributeNS(null,"y", pathBBox.y + pathBBox.height/2);
newText.setAttributeNS(null,"font-size","10");
var textNode = document.createTextNode(i+1);
newText.appendChild(textNode);
document.getElementById("myIdInfo").appendChild(newText);
});
});
I tried using getBBox() method but it is not working for all shapes.
In above image, the number 4 should be inside the hollow C shape.
Solution 1:[1]
Each shape is different center of the box may not be inside the actual shape. For those hollow shapes there are no exact centers. You just need to find any suitable point that falls inside the shape.
Considering center of the box as origin we can try to find points that intersect with x and y axis. And pick a point in between two intersections:
Demo on codesandbox. It'll take some time to load.
let sv = document.querySelector('svg');
let colorGroup = ['#700843', '#E95C87', '#AED9C5', '#FDFCFD', '#F8AF3F'];
colorGroup.forEach(function (e, i) {
$(`path[data-color-group = "${e}"]`).each(function (ii, aa) {
let path = $(this)[0]
if (i == 3) {
path.setAttribute('style', `fill: #cf0acf`);
} else if (i == 0) {
path.setAttribute('style', `fill: #a35a83`);
} else {
path.setAttribute('style', `fill: ${e}`);
}
//console.log(pathBBox, path);
let getPathData = $(this).attr("data-index");
point = getInsidePoint(path);
var svgNS = "http://www.w3.org/2000/svg";
var newText = document.createElementNS(svgNS, "text");
newText.setAttributeNS(null, "x", point.x);
newText.setAttributeNS(null, "y", point.y);
newText.setAttributeNS(null, "font-size", "10");
var textNode = document.createTextNode(i + 1);
newText.appendChild(textNode);
document.getElementById("myIdInfo").appendChild(newText);
});
});
// figure out point insice the shape
function getInsidePoint(path) {
let pathBBox = path.getBBox();
let point = sv.createSVGPoint();
point.x = Math.floor(pathBBox.x + pathBBox.width / 2);
point.y = Math.floor(pathBBox.y + pathBBox.height / 2);
// if mid point is inside then return
if (path.isPointInFill(point)) {
return point;
}
let result = sv.createSVGPoint();
let l = path.getTotalLength();
let h = []; // horizontal intersections
let v = []; // vertical intersections
// find intersecting points along x and y axis
for (let i = 0; i < l; i++) {
let p = path.getPointAtLength(i);
p.x = Math.floor(p.x)
p.y = Math.floor(p.y);
if (p.x == point.x) v.push(p);
if (p.y == point.y) h.push(p);
}
// whichever axis has less intersecitions
// get center point of the intersection
try {
if (h.length < v.length) {
result.y = point.y;
// find out which point in between two intersection points falls inside
for (let i = 0; i < h.length - 1; i++) {
result.x = Math.abs(h[i + 1].x - h[i].x) / 2;
if (result.x < 2) continue;
result.x += Math.min(h[i + 1].x, h[i].x);
if (path.isPointInFill(result)) {
break;
}
}
} else {
result.x = point.x;
for (let i = 0; i < v.length - 1; i++) {
result.y = Math.abs(v[i + 1].y - v[i].y) / 2;
if (result.y < 2) continue;
result.y += Math.min(v[i + 1].y, v[i].y);
if (path.isPointInFill(result)) {
break;
}
}
}
} catch (e) {
// ignore errors for dots and open shapes
}
return result;
}
Note, play with the getInsidePoint function to adjust number positions. This isn't the most efficient implementation, refer this answer for more efficient implementation approach.
Possible improvements could be:
- Find out better points manually for each shape and put them in
data-*attributes in path elements:
<path data-label-x='10' data-label-y='40' ...>...</path>
and use these co-ordinates. No need to calculate on client side every time.
- If the diagram is scalable then mark similar shapes. And do calculations for only one shape and use the same point for all similar shapes considering rotation.
Solution 2:[2]
Since your graphic (mandala) has a radial layout, you might also try to find angle based intersections.
We can assume that shapes which don't have fill intersecting centers will have some intersections around the center x/y.
Essentially, we're also using isPointInFill() to first exclude already fine coordinates within the shape's fill area and then find appropriate (x/y shifted) alternatives for all exceptions.
This approach provides some "best match" logic – i.e. prefer intersecting coordinates with more surrounding space. Illustrated above by green dots: more intersections (array.length based) will be preferred to calculate the label's x/y coordinates.
We can drastically reduce the number of "checkpoints" by analyzing only some predictable and also prioritized (angle based) points around the shapes's center (retrieved by getBBox()).
This is still quite expensive regarding performance due to the graphic's complexity.
Example check points around center:
let svg = document.querySelector('svg');
let labelGroup = svg.querySelector('#myIdInfo');
let colorGroup = ['#CF0ACF', '#E95C87', '#AED9C5', '#FDFCFD', '#F8AF3F'];
let decimals = 1;
let labelGroupMarkup = '';
//get svg viewBox dimensions for rotation checks;
let svgBB = svg.getAttribute('viewBox').split(' ');
let [svgCx, svgCy] = [(svgBB[0] + svgBB[2]) / 2, (svgBB[1] + svgBB[3] / 2)];
// count point checks for debugging
let checks = 0;
let pointHits = 0;
let notInFill = 0;
// loop colors
colorGroup.forEach(function(color, colorIndex) {
// find shapes
let shapes = svg.querySelectorAll('[data-color-group="' + color + '"] path');
shapes.forEach(function(shape, i) {
let bb = shape.getBBox();
let [x, y, w, h] = [bb.x, bb.y, bb.width, bb.height];
let cx = +(x + w / 2).toFixed(decimals);
let cy = +(y + h / 2).toFixed(decimals);
let l = +(x).toFixed(decimals);
let r = +(x + w).toFixed(decimals);
let t = +(y).toFixed(decimals);
let b = +(y + h).toFixed(decimals);
// label coordinates in fill?
let inFill = checkPointIntersection(svg, shape, [cx, cy]);
checks++;
//not in fill: find alternative coordinates
if (!inFill) {
notInFill++; //
let rMid = (w + h) / 2;
let angles = [0, 45, 90, 135, 180, 225, 270, 315];
// check quarter position to reduce angle checkpoints
let classRotate = '';
if (t < svgCy && b < svgCy && l < svgCx && r > svgCx) {
classRotate = 'topCenter';
angles = [0, 180]
} else if (l > svgCx && b < svgCy) {
classRotate = 'topRight';
angles = [45, 90];
} else if (l > svgCx && b > svgCy && t < svgCy && b > svgCy) {
classRotate = 'centerRight';
angles = [90, 0]
} else if (l > svgCx && t > svgCy) {
classRotate = 'bottomRight';
angles = [135, 180];
} else if (l < svgCx && t > svgCy && r > svgCx) {
classRotate = 'bottomCenter';
angles = [180, 0];
} else if (r < svgCx && t > svgCy) {
classRotate = 'bottomLeft';
angles = [225, 270];
} else if (r < svgCx && b > svgCy) {
classRotate = 'centerLeft';
angles = [270, 0];
} else if (r < svgCx && b < svgCy) {
classRotate = 'topLeft';
angles = [315, 270];
} else {
classRotate = 'center';
angles = [0];
}
//shape.classList.add(classRotate);
let labeCoords = checkCircleIntersects(svg, shape, angles, cx, cy, rMid / 4);
cx = labeCoords[0]
cy = labeCoords[1]
}
let value = colorIndex;
labelGroupMarkup +=
`<text x="${cx}" y="${cy}" dy="2" >${value}</text>`;
//test eventListener: set colors by color group
shape.addEventListener('click', function(e) {
let current = e.currentTarget;
let g = current.closest('[data-color-group]');
let color = g.getAttribute('data-color-group');
let shapeColor = current.getAttribute('fill');
if (!shapeColor) {
current.setAttribute('fill', color)
} else {
current.removeAttribute('fill')
}
})
})
// add labels
labelGroup.innerHTML = labelGroupMarkup;
});
console.log('totalchecks: ' + checks + ' notInFill: ' + notInFill + ' pointHits: ' + pointHits)
// helpers
function checkCircleIntersects(svg, shape, percentages, cx, cy, r, percentageToAngel = false) {
let interpoints = [];
let interpointspercent = [];
percentages.forEach(function(percent) {
// check intersections on for different radii
let pArr = [];
let steps = [0.75, 1, 1.25, 1.5, 1.75, 2, 2.25];
for (let i = 0; i < steps.length; i++) {
let p = getPosOnCircle(cx, cy, percent, r * steps[i], percentageToAngel);
let inFill = checkPointIntersection(svg, shape, p);
checks++;
if (inFill) {
pArr.push(p);
pointHits++;
}
}
// add matches to array
interpointspercent.push(pArr);
});
// return best match x/y coordinates
let bestMatchArr = getBestMatchArr(interpointspercent);
let bestMatch = getMidPoints(bestMatchArr);
if (!bestMatch.length) {
shape.setAttribute('fill', 'red')
console.log(`no match: ${cx} ${cy}`)
bestMatch = [cx, cy]
}
return bestMatch
}
function checkPointIntersection(svg, shape, coords, strokeIntersection = false) {
let svgP = svg.createSVGPoint();
svgP.x = coords[0];
svgP.y = coords[1];
let inFill = shape.isPointInFill(svgP);
let inStroke = strokeIntersection ? shape.isPointInStroke(svgP) : false;
//let inStroke = shape.isPointInStroke(svgP);
let intersecting = false;
if (inFill && !inStroke) {
intersecting = true;
}
return intersecting;
}
// helper: get x/y coordinates according to angle or percentage
function getPosOnCircle(cx, cy, value, radius, valueToPercent = false, decimals = 3, offset = -90) {
// convert percentages to angles
let angle = valueToPercent ? 360 / (100 / value) + offset : value + offset;
let x = +(cx + Math.cos((angle * Math.PI) / 180) * radius).toFixed(decimals);
let y = +(cy + Math.sin((angle * Math.PI) / 180) * radius).toFixed(decimals);
return [x, y];
}
// find longest intersection array
function getBestMatchArr(arrays) {
let max = 0;
let longestArr = [];
for (let i = 0; i < arrays.length; i++) {
let current = arrays[i];
let len = current.length;
if (len > max) {
max = len;
}
}
for (let i = 0; i < arrays.length; i++) {
let current = arrays[i];
let len = current.length;
if (len == max) {
longestArr.push(current);
}
}
let midIndex = longestArr.length > 1 ? Math.floor((longestArr.length - 1) / 2) : 0;
return longestArr[midIndex];
}
// interpolate first and last x/y
function getMidPoints(coords) {
let l = coords.length - 1;
let midIndex = Math.floor(l / 2);
let [x1, y1] = coords[0];
let [x2, y2] = coords[coords.length - 1];
let middle = [ +((x1 + x2) / 2).toFixed(1) , +((y1 + y2) / 2).toFixed(1) ];
return middle;
}
// debug helper: render coordinates as markers
function renderPoint(svg, coords, fill = 'red', r = '2') {
let marker =
'<circle cx="' +
coords[0] +
'" cy="' +
coords[1] +
'" r="' + r + '" fill="' + fill + '" ><title>' +
coords.join(", ") +
"</title></circle>";
svg.insertAdjacentHTML("beforeend", marker);
}
svg {
width: 50em;
border: 1px solid #ccc;
display: block;
}
text {
font-family: Arial;
font-weight: bold;
font-size: 6px;
text-shadow: -0.1em -0.1em 0 #fff, -0.1em 0 0 #fff, 0em -0.1em 0 #fff, 0em 0.1em 0 #fff, 0.1em -0.1em 0 #fff, -0.1em 0.1em 0 #fff, 0.1em 0.1em 0 #fff;
}
<svg viewBox="0 0 1144.4 1144.4">
<g class="wrap-stroke" stroke-width="1" stroke="#000" fill="#fff">
<g data-color-group="#E95C87" data-fillable="true">
<path d="M572.2 381.5c-14.5 0-27.7 3-37 15.2-7 9.2-9.4 20.5-7.7 31.7a65 65 0 0 1-18.7-17.1c5.3-21.6 25-35.2 37.9-52.3a172.1 172.1 0 0 0 25.5-49 172.1 172.1 0 0 0 25.5 49c12.9 17.1 32.5 30.7 37.9 52.3a65 65 0 0 1-18.7 17.1 41.3 41.3 0 0 0-7.7-31.7c-9.3-12.2-22.5-15.2-37-15.2z"/>
<path d="M707 437.3c-10.2-10.2-21.7-17.4-36.9-15.4a41.3 41.3 0 0 0-27.8 17c-2-8.3-2.4-16.9-1.1-25.3 19-11.5 42.6-7.2 63.8-10.2 18.4-2.6 36.1-8.4 52.6-16.6-8.2 16.6-14 34.2-16.6 52.6-3 21.2 1.3 44.7-10.2 63.8-8.4 1.3-17 .9-25.3-1.1a41.9 41.9 0 0 0 17-27.8c1.9-15.3-5.3-26.7-15.5-37z"/>
<path d="M437.4 437.3C427.2 447.5 420 459 422 474.2a41.3 41.3 0 0 0 17 27.8c-8.3 2-16.9 2.4-25.3 1.1-11.5-19-7.2-42.6-10.2-63.8a173.5 173.5 0 0 0-16.6-52.6c16.6 8.2 34.2 14 52.6 16.6 21.2 3 44.7-1.3 63.8 10.2 1.3 8.4.9 17-1.1 25.3a41.9 41.9 0 0 0-27.8-17c-15.3-1.9-26.7 5.3-37 15.5z"/>
<path d="M572.2 115.6a18.6 18.6 0 0 0-15 30 20.6 20.6 0 0 0-.5 27.9 20.6 20.6 0 0 0-6.5 23 85.8 85.8 0 0 0-33.4-7.7c-6.5-19.9-11.2-40.6 3.3-58.3a65.2 65.2 0 0 1 52.1-23.5c20.3 0 39 7.5 52.1 23.5 14.5 17.7 9.8 38.3 3.3 58.3-11.5.3-22.9 3-33.4 7.7 3.2-8.6.2-17.5-6.5-23 7.2-8 7-20.2-.5-27.9 9.3-12.3 1.2-30-15-30z"/>
<path d="M895 249.3a18.6 18.6 0 0 0-31.9 10.6c-9.9.1-19.2 7.7-20.1 19.4-8.6-.8-17 3.4-20.8 11.7a88 88 0 0 0-18.1-29.1c9.5-18.7 20.7-36.6 43.6-38.9 20.6-2 39.1 5.9 53.4 20.2a65.7 65.7 0 0 1 20.2 53.4c-2.3 22.8-20.2 34-38.9 43.6a86.3 86.3 0 0 0-29.1-18.1c8-3.7 12.7-12.1 11.7-20.8a20.3 20.3 0 0 0 19.4-20.1 18.5 18.5 0 0 0 10.6-31.9z"/>
<path d="M249.4 249.3a18.6 18.6 0 0 0 10.6 31.9c.1 9.9 7.7 19.2 19.4 20.1-.8 8.6 3.4 17 11.7 20.8a88 88 0 0 0-29.1 18.1c-18.7-9.5-36.6-20.7-38.9-43.6a65.6 65.6 0 0 1 20.2-53.4 65.6 65.6 0 0 1 53.4-20.2c22.8 2.3 34 20.2 43.6 38.9a86.3 86.3 0 0 0-18.1 29.1 20.4 20.4 0 0 0-20.8-11.7 20.3 20.3 0 0 0-20.1-19.4 18.5 18.5 0 0 0-31.9-10.6z"/>
<path d="M572.2 472.3c-3.8-9.5-9.8-18-17.6-24.6-5.8-7.8-10-16.5-7.3-26.5 3-11.1 13.9-16.3 24.9-16.2 11-.2 21.9 5.1 24.9 16.2 2.7 10-1.5 18.7-7.3 26.5a62.7 62.7 0 0 0-17.6 24.6z"/>
</g>
<g data-color-group="#AED9C5" data-fillable="true">
<path d="M572.2 268.5a17.8 17.8 0 0 0-17.9 17.7c0 8.4 6 15.3 13.8 17.2a136.8 136.8 0 0 1-29.8 48.9 33 33 0 0 1-21.5-29.7 70.6 70.6 0 0 1 11.8-40.6c12.9-19.5 31.3-34.6 43.5-54.6 12.2 20 30.6 35.1 43.5 54.6a69.1 69.1 0 0 1 11.8 40.6 33 33 0 0 1-21 29.5l-.5.2a136.8 136.8 0 0 1-29.8-48.9 18 18 0 0 0 13.8-17.2c.2-9.8-8-17.8-17.7-17.7z"/>
<path d="M786.9 357.4c-6.8-6.9-18.3-7-25.1-.2a18 18 0 0 0-2.4 21.9c-17.4 8.4-36.4 13-55.7 13.5l-.2-.5a33 33 0 0 1 6-35.7 68.6 68.6 0 0 1 37.1-20.3c23-4.7 46.6-2.3 69.4-7.8-5.5 22.8-3.2 46.5-7.8 69.4a70.3 70.3 0 0 1-20.3 37.1 33 33 0 0 1-35.7 6l-.5-.2c1.4-19.3 4.3-38.4 13.5-55.7 6.8 4.2 16 3.5 21.9-2.4 6.8-7 6.8-18.2-.2-25.1z"/>
<path d="M357.5 357.4c-6.9 6.8-7 18.3-.2 25.1a18 18 0 0 0 21.9 2.4c8.4 17.4 13 36.4 13.5 55.7l-.5.2a33 33 0 0 1-35.7-6 68.6 68.6 0 0 1-20.3-37.1c-4.7-23-2.3-46.6-7.8-69.4 22.8 5.5 46.5 3.2 69.4 7.8a70.3 70.3 0 0 1 37.1 20.3 33 33 0 0 1 6 35.7l-.2.5c-19.3-.5-38.3-5.1-55.7-13.5 4.2-6.8 3.5-16-2.4-21.9-7-6.8-18.2-6.7-25.1.2z"/>
</g>
<g data-color-group="#FDFCFD" data-fillable="true">
<path d="M572.2 402.4c-11.1 0-23.3 5.1-27.3 16.2-3 8.2-1 16.3 3 23.5-5.1-4-11.2-7.7-17.2-11.6a39 39 0 0 1 8.4-34.2c9.1-10 20.4-12.1 33.2-12.1s24.1 2.1 33.2 12.1a39 39 0 0 1 8.4 34.2c-6 3.9-12.1 7.6-17.2 11.6 3.9-7.2 6-15.3 3-23.5-4.2-11.1-16.3-16.2-27.5-16.2z"/>
<path d="M692.2 452.1c-7.9-7.9-20-12.8-30.8-7.9a27.8 27.8 0 0 0-14.5 18.7c-.8-6.4-2.5-13.4-3.9-20.4a38.9 38.9 0 0 1 30.1-18.2c13.5-.7 23 5.9 32 14.9s15.6 18.5 14.9 32c-.6 12.5-8 23.4-18.2 30.1-7-1.5-14-3.1-20.4-3.9 7.9-2.3 15.1-6.6 18.7-14.5 5-10.8 0-22.9-7.9-30.8z"/>
<path d="M452.2 452.1c-7.9 7.9-12.8 20-7.9 30.8a27.8 27.8 0 0 0 18.7 14.5c-6.4.8-13.4 2.5-20.4 3.9a38.9 38.9 0 0 1-18.2-30.1c-.7-13.5 5.9-23 14.9-32s18.5-15.6 32-14.9c12.5.6 23.4 8 30.1 18.2-1.5 7-3.1 14-3.9 20.4-2.3-7.9-6.6-15.1-14.5-18.7-10.8-5-22.9 0-30.8 7.9z"/>
<path d="M563.7 551.7a27 27 0 0 1 17 0c5.2 2.6 9.4 6.8 12 12 .9 2.8 1.4 5.7 1.4 8.5s-.5 5.7-1.4 8.5a26.8 26.8 0 0 1-12 12 27 27 0 0 1-17 0 26.8 26.8 0 0 1-12-12c-.9-2.8-1.4-5.7-1.4-8.5s.5-5.7 1.4-8.5c2.6-5.2 6.8-9.4 12-12zm8.5 5.8a16 16 0 0 0-5.3.9l-.3.1-.3.1a17.1 17.1 0 0 0-7.6 7.6l-.1.3-.1.3a16 16 0 0 0 0 10.6l.1.3.1.3a17.1 17.1 0 0 0 7.6 7.6l.3.1.3.1a16 16 0 0 0 10.6 0l.3-.1.3-.1a17.1 17.1 0 0 0 7.6-7.6l.1-.3.1-.3a16 16 0 0 0 0-10.6l-.1-.3-.1-.3a17.1 17.1 0 0 0-7.6-7.6l-.3-.1-.3-.1a16 16 0 0 0-5.3-.9z"/>
<path d="M572.2 210.7a93.6 93.6 0 0 0-13.1-9.6l-.4-.7c-10.7-12-1-27.1 13.5-27.1s24.2 15.1 13.5 27.1l-.4.7c-4.6 2.8-9 6-13.1 9.6z"/>
<path d="M827.7 316.6a86 86 0 0 0-2.5-16l.2-.8c.9-16 18.4-19.9 28.7-9.6 10.3 10.3 6.5 27.8-9.6 28.7l-.8.2a78.4 78.4 0 0 0-16-2.5z"/>
<path d="M316.7 316.6a86 86 0 0 0-16 2.5l-.8-.2c-16-.9-19.9-18.4-9.6-28.7s27.8-6.5 28.7 9.6l.2.8a78.4 78.4 0 0 0-2.5 16z"/>
<path d="M572.2 168.1c-4.1 0-7.8.9-11 2.5a15 15 0 0 1 11-25.9 15 15 0 0 1 11 25.9 26 26 0 0 0-11-2.5z"/>
<path d="M857.9 286.5c-2.7-2.7-6-4.8-9.6-6a15 15 0 0 1 26.1-10.5 15 15 0 0 1-10.5 26.1 24.8 24.8 0 0 0-6-9.6z"/>
<path d="M286.6 286.5c-2.7 2.7-4.8 6-6 9.6a15 15 0 0 1-10.5-26.1 15 15 0 0 1 26.1 10.5c-3.7 1.2-7 3.2-9.6 6z"/>
<path d="M572.2 139.4a22 22 0 0 0-10.9 2.9 13.4 13.4 0 0 1 10.9-21.5 13.3 13.3 0 0 1 10.9 21.5 22 22 0 0 0-10.9-2.9z"/>
<path d="M878.1 266.2a21.2 21.2 0 0 0-9.8-5.7 13.3 13.3 0 0 1 22.9-7.5 13.3 13.3 0 0 1-7.5 22.9c-.8-3.4-2.6-6.8-5.6-9.7z"/>
<path d="M266.3 266.2a21.2 21.2 0 0 0-5.7 9.8 13.3 13.3 0 0 1-7.5-22.9 13.3 13.3 0 0 1 22.9 7.5c-3.4.8-6.8 2.6-9.7 5.6z"/>
<path d="m572.2 298.5-1.3-.1c-13.2-1.3-12.7-22.2 1.3-22.1 14-.1 14.5 20.8 1.3 22.1l-1.3.1z"/>
<path d="m765.7 378.6-.9-1c-8.4-10.3 6.7-24.6 16.5-14.7 10 9.8-4.4 25-14.7 16.5-.3-.1-.6-.4-.9-.8z"/>
<path d="m378.7 378.6-1 .9c-10.3 8.4-24.6-6.7-14.7-16.5 9.8-10 25 4.4 16.5 14.7-.1.3-.4.6-.8.9z"/>
<path d="M572.2 68c-14.6 0-14.5-22.3 0-22.1 14.5-.2 14.6 22.1 0 22.1z"/>
<path d="M928.7 215.6c-10.3-10.3 5.5-26 15.7-15.7 10.2 10.2-5.4 26-15.7 15.7z"/>
<path d="M215.7 215.6c-10.3 10.3-26-5.5-15.7-15.7 10.2-10.2 26 5.4 15.7 15.7z"/>
<path d="M472 330.1c18.2 14.1 38.9 23 61.3 27.9l-.4.4c-4.3 4.8-8.6 9.5-12.8 14.4a130.5 130.5 0 0 1-37.8-20.8c-.5-.4-1-.4-1.4-.3-.4.2-.8.6-.8 1.2a133.7 133.7 0 0 1-12 41.5c-6.4-.5-12.8-.8-19.2-1.1h-.5a153.1 153.1 0 0 0 23.6-63.2z"/>
<path d="M672.4 330.1c3.1 22.5 11.1 44 23.6 63h-.5c-6.4.4-12.8.6-19.2 1.1a128.5 128.5 0 0 1-12-41.5c-.1-.6-.4-1-.8-1.2s-1-.1-1.4.3c-11.4 9-24.2 16.1-37.9 20.8-4.1-4.9-8.5-9.6-12.8-14.4l-.3-.4a147.9 147.9 0 0 0 61.3-27.7z"/>
<path d="M461.6 305c15.6 12.1 33 20.9 51.8 26.8 2.9 9.7 9 18.1 18.6 23.2-21.9-5-42.4-14-60.1-28.2-.5-.4-1-.4-1.4-.3-.4.2-.8.6-.8 1.2a149 149 0 0 1-22.5 62.5c3-9.9 1.8-20.6-3.2-29.6a168 168 0 0 0 17.6-55.6z"/>
<path d="M682.9 305a163 163 0 0 0 17.7 55.6c-5 9-6.2 19.7-3.2 29.6-12-19-20.1-39.9-22.5-62.5-.1-.6-.4-1-.8-1.2-.4-.2-1-.1-1.4.3a146.6 146.6 0 0 1-60.1 28.2 39 39 0 0 0 18.7-23.2 164.9 164.9 0 0 0 51.6-26.8z"/>
<path d="M443.3 260.8a177.2 177.2 0 0 0 72.4 31.8h.1l-1 2.5a67 67 0 0 0-3 12.5 170.3 170.3 0 0 1-60.7-31.2c-.5-.4-1-.4-1.4-.3-.4.2-.8.6-.8 1.2a174 174 0 0 1-20.9 65 65.1 65.1 0 0 0-11-6.8l-2.5-1.1.1-.1c15.1-22.1 24.6-47 28.7-73.5z"/>
<path d="M701.2 260.8a177.4 177.4 0 0 0 28.7 73.7l.1.1-2.5 1.1a65.1 65.1 0 0 0-11 6.8 174 174 0 0 1-20.9-65c-.1-.6-.4-1-.8-1.2-.4-.2-1-.1-1.4.3a177.9 177.9 0 0 1-60.7 31.2c-.6-4.3-1.5-8.5-3-12.5l-1-2.5h.1a181 181 0 0 0 72.4-32z"/>
<path d="M451.2 279.9c18.1 14.1 38.4 24 60.4 30.4l-.3 5.1c-.1 4.5.3 9 1.2 13.3-18.6-6-35.7-14.8-51-27.1-.5-.4-1-.4-1.4-.3-.4.2-.8.6-.8 1.2a158 158 0 0 1-16.9 55.3 52.4 52.4 0 0 0-8.5-10.3l-3.8-3.4c11-19.9 18.1-41.7 21.1-64.2-.1 0 0 0 0 0z"/>
<path d="M693.3 279.9a178.1 178.1 0 0 0 21.2 64.2 59.5 59.5 0 0 0-12.3 13.7 158 158 0 0 1-16.9-55.3c-.1-.6-.4-1-.8-1.2-.4-.2-1-.1-1.4.3a157.7 157.7 0 0 1-51 27.1c1.5-6 1.8-12.3.9-18.4a177.7 177.7 0 0 0 60.3-30.4z"/>
<path d="M482.4 355.2c11 8.5 23 14.9 36 19.6-3.4 4.1-6.6 8.4-9.4 13-5.7-3.1-11.2-6.7-16.3-10.8-.5-.4-1-.4-1.4-.3-.4.2-.8.6-.8 1.2-.7 6.5-2 12.9-3.8 19.2a121 121 0 0 0-15.8-2.6c5.7-12.5 9.7-25.5 11.5-39.3z"/>
<path d="M662 355.2c1.8 13.6 5.7 26.9 11.6 39.3-5.4.5-10.6 1.3-15.8 2.6-1.8-6.3-3.1-12.6-3.8-19.2-.1-.6-.4-1-.8-1.2-.4-.2-1-.1-1.4.3-5.1 4.1-10.5 7.7-16.3 10.8-2.8-4.6-6-8.9-9.4-13 13-4.7 24.9-11.1 35.9-19.6z"/>
<path d="M492.8 380.4c4.7 3.6 9.6 6.9 14.8 9.8-2.3 4-4.2 8.2-5.6 12.6a66.2 66.2 0 0 0-12.8-4.9c1.6-5.7 2.8-11.6 3.6-17.5z"/>
<path d="M651.6 380.3c.7 5.9 1.9 11.7 3.6 17.4a66.2 66.2 0 0 0-12.8 4.9 64.5 64.5 0 0 0-5.6-12.6c5.2-2.7 10.1-6 14.8-9.7z"/>
</g>
<g data-color-group="#F8AF3F" data-fillable="true">
<path d="M572.2 98.6c-4 0-7.9.3-11.8.9 2.2-4.1 2.2-8.9.4-13.7-4.8-12.8-18.5-21-15-34.2 10-11.4 18.7-23.7 26.4-36.8 7.6 13 16.5 25.4 26.4 36.8 3.5 13.3-10.1 21.4-15 34.2-1.8 4.8-1.8 9.6.4 13.7-3.9-.6-7.8-.9-11.8-.9zm0-58.1c-9-.1-16.5 7.4-16.5 16.3a16.5 16.5 0 0 0 33 0c.1-8.9-7.5-16.3-16.5-16.3z"/>
<path d="M907 237.3a73.2 73.2 0 0 0-9-7.7 16 16 0 0 0 9.9-9.4c5.6-12.5 1.8-27.9 13.6-34.8 15.1-1 30-3.6 44.7-7.4a249.8 249.8 0 0 0-7.4 44.7c-6.9 11.9-22.3 8-34.8 13.6a16 16 0 0 0-9.4 9.9 71.5 71.5 0 0 0-7.6-8.9zm41.1-41.1a16.5 16.5 0 0 0-23.3-.1 16.6 16.6 0 0 0 .1 23.3 16.6 16.6 0 0 0 23.3.1c6.4-6.4 6.3-17-.1-23.3z"/>
<path d="M237.4 237.3a73.2 73.2 0 0 0-7.7 9 16 16 0 0 0-9.4-9.9c-12.5-5.6-27.9-1.8-34.8-13.6-1-15.1-3.6-30-7.4-44.7 14.7 3.8 29.6 6.3 44.7 7.4 11.9 6.9 8 22.3 13.6 34.8a16 16 0 0 0 9.9 9.4 64.9 64.9 0 0 0-8.9 7.6zm-41.1-41.1a16.5 16.5 0 0 0-.1 23.3 16.6 16.6 0 0 0 23.3-.1c6.3-6.3 6.5-16.9.1-23.3-6.4-6.4-17-6.2-23.3.1z"/>
<path d="M568.6 563.4c2.3-.8 4.9-.8 7.2 0 2.2 1.2 4 3 5.2 5.2.8 2.3.8 4.9 0 7.2a13 13 0 0 1-5.2 5.2c-2.3.8-4.9.8-7.2 0a13 13 0 0 1-5.2-5.2c-.8-2.3-.8-4.9 0-7.2 1.2-2.2 3-4 5.2-5.2z"/>
<path d="M530.1 67.8c.9 12.3 19 26.8 11.4 36.8a72 72 0 0 0-10.7 5.5c1-3.5 1.3-7.2.6-11.1-1-5.8-10.6-17.8-8.1-24.8l6.8-6.4z"/>
<path d="M185.9 245.3c9.4 8.1 32.4 5.6 34 18a75.5 75.5 0 0 0-3.6 11.4c-1.8-3.2-4.2-6-7.4-8.3-4.8-3.3-20.1-5.1-23.3-11.9l.3-9.2z"/>
<path d="M899 185.8c-8.1 9.4-5.6 32.4-18 34a75.5 75.5 0 0 0-11.4-3.6c3.2-1.8 6-4.2 8.3-7.4 3.3-4.8 5.1-20.1 11.9-23.3l9.2.3z"/>
<path d="M614.3 67.8c-.9 12.3-19 26.8-11.4 36.8a72 72 0 0 1 10.7 5.5c-1-3.5-1.3-7.2-.6-11.1 1-5.8 10.6-17.8 8.1-24.8l-6.8-6.4z"/>
<path d="M245.4 185.8c8.1 9.4 5.6 32.4 18 34 3.7-1.5 7.5-2.8 11.4-3.6-3.2-1.8-6-4.2-8.3-7.4-3.3-4.8-5.1-20.1-11.9-23.3l-9.2.3z"/>
<path d="M958.5 245.3c-9.4 8.1-32.4 5.6-34 18 1.5 3.7 2.8 7.5 3.6 11.4 1.8-3.2 4.2-6 7.4-8.3 4.8-3.3 20.1-5.1 23.3-11.9l-.3-9.2z"/>
<path d="M508.1 88.7c-.1 10.9 12.7 21.7 9.2 32.6-1.5 1.6-3 3.3-4.4 5.2-.4-1.9-1.2-3.8-2.3-5.4-1.5-2.4-3.7-4-5.7-6-5-4.7-7.8-10.1-6.1-16.3a104 104 0 0 1 7.1-7.8l2.2-2.3z"/>
<path d="M185.1 275.6c7.6 7.7 24.3 6.4 29.6 16.6.1 2.2.3 4.5.6 6.7-1.6-1-3.4-1.8-5.5-2.2-2.8-.6-5.5-.2-8.3-.2-6.9.2-12.7-1.7-15.8-7.2l-.6-10.5v-3.2z"/>
<path d="M868.7 185c-7.7 7.6-6.4 24.3-16.6 29.6-2.2.1-4.5.3-6.7.6 1-1.6 1.8-3.4 2.2-5.5.6-2.8.2-5.5.2-8.3-.2-6.9 1.7-12.7 7.2-15.8 4.6-.4 9.1-.6 13.7-.6z"/>
<path d="M636.3 88.7c.1 10.9-12.7 21.7-9.2 32.6 1.5 1.6 3 3.3 4.4 5.2.4-1.9 1.2-3.8 2.3-5.4 1.5-2.4 3.7-4 5.7-6 5-4.7 7.8-10.1 6.1-16.3-2.2-2.7-4.6-5.3-7-7.8l-2.3-2.3z"/>
<path d="M275.7 185c7.7 7.6 6.4 24.3 16.6 29.6 2.2.1 4.5.3 6.7.6-1-1.6-1.8-3.4-2.2-5.5-.6-2.8-.2-5.5-.2-8.3.2-6.9-1.7-12.7-7.2-15.8l-10.5-.6h-3.2z"/>
<path d="M959.3 275.6c-7.6 7.7-24.3 6.4-29.6 16.6-.1 2.2-.3 4.5-.6 6.7 1.6-1 3.4-1.8 5.5-2.2 2.8-.6 5.5-.2 8.3-.2 6.9.2 12.7-1.7 15.8-7.2l.6-10.5v-3.2z"/>
<path d="M505.1 173.5c.7 3.4 1.6 6.8 2.6 10.2-7.1-3.6-16.5-3.4-22.2-8.4a66.6 66.6 0 0 1-4.2-13.8c.9.8 1.8 1.5 2.8 2.1 3.4 1.9 7.2 2.1 10.8 3.2 4.4 1.3 7.7 3.5 10.2 6.7z"/>
<path d="M242.9 337.7c2.9 1.9 6 3.7 9.1 5.4-7.5 2.5-14.1 9.3-21.7 9.7a77.5 77.5 0 0 1-12.7-6.8c1.2 0 2.4-.2 3.5-.5 3.8-1 6.6-3.6 9.9-5.4 3.9-2.1 7.9-2.9 11.9-2.4z"/>
<path d="M806.6 242.8c-1.9 2.9-3.7 6-5.4 9.1-2.5-7.5-9.3-14.1-9.7-21.7 1.9-4.4 4.1-8.7 6.8-12.7 0 1.2.2 2.3.5 3.5 1 3.8 3.6 6.6 5.4 9.9 2.1 3.9 2.9 7.9 2.4 11.9z"/>
<path d="M639.3 173.5c-.7 3.4-1.6 6.8-2.6 10.2 7.1-3.6 16.5-3.4 22.2-8.4 1.9-4.6 3.3-9.2 4.2-13.8-.8.8-1.8 1.5-2.8 2.1-3.4 1.9-7.2 2.1-10.8 3.2-4 1.1-7.6 3.4-10.2 6.7z"/>
<path d="M337.8 242.8c1.9 2.9 3.7 6 5.4 9.1 2.5-7.5 9.3-14.1 9.7-21.7a77.5 77.5 0 0 0-6.8-12.7c0 1.2-.2 2.4-.5 3.5-1 3.8-3.6 6.6-5.4 9.9-2 3.6-2.9 7.8-2.4 11.9z"/>
<path d="M901.5 337.7c-2.9 1.9-6 3.7-9.1 5.4 7.5 2.5 14.1 9.3 21.7 9.7 4.4-1.9 8.7-4.1 12.7-6.8-1.2 0-2.4-.2-3.5-.5-3.8-1-6.6-3.6-9.9-5.4-3.6-2-7.8-2.9-11.9-2.4z"/>
<path d="M504.5 144c-.9 3.5-1.3 7.1-1.4 10.7-1.8-1.8-4-3.3-6.2-4.7-7-4.2-17.6-8.9-14.9-19v-.2c.5-2.4 1.2-4.8 2-7.2l1.9-4.6c.2 2.7.9 5.4 2.3 8 3.2 6.5 12.1 10.9 16.3 17z"/>
<path d="M221.6 317.3c1.9 3.1 4.1 5.9 6.5 8.5-2.6.1-5.2.4-7.7 1.1-7.9 2-18.8 6.2-24-2.9l-.1-.1-3.7-6.5-1.9-4.5c2 1.7 4.5 3.1 7.3 4 7 2.2 16.3-.9 23.6.4z"/>
<path d="M827 221.5c-3 1.9-5.9 4.1-8.5 6.5-.1-2.6-.4-5.2-1.1-7.7-2-7.9-6.2-18.8 2.9-24l.1-.1 6.5-3.7 4.5-1.9c-1.7 2-3.1 4.5-4 7.3-2.2 7 1 16.3-.4 23.6z"/>
<path d="M639.9 144a55 55 0 0 1 1.4 10.7c1.8-1.8 4-3.3 6.2-4.7 7-4.2 17.6-8.9 14.9-19v-.2c-.6-2.4-1.2-4.8-2-7.2l-1.9-4.6c-.2 2.7-1 5.4-2.3 8-3.2 6.5-12 10.9-16.3 17z"/>
<path d="M317.4 221.5c3.1 1.9 5.9 4.1 8.5 6.5 0-2.6.5-5.1 1.1-7.7 2-7.9 6.2-18.8-2.9-24l-.1-.1-6.5-3.7-4.5-1.9c1.7 2 3.1 4.5 4 7.3 2.2 7-.9 16.3.4 23.6z"/>
<path d="M922.8 317.3c-1.9 3-4.1 5.9-6.5 8.5 2.6 0 5.1.5 7.7 1.1 7.9 2 18.8 6.2 24-2.9l.1-.1 3.7-6.5 1.9-4.5c-2 1.7-4.5 3.1-7.3 4-6.9 2.2-16.3-.9-23.6.4z"/>
</g>
</g>
<g class="myIdInfo" id="myIdInfo" text-anchor="middle"/>
</svg>
helpers:
getPosOnCircle()– get arc coordinates (based upon Paul LeBeau's answer – Pure svg pie chart, text align center ) – also helpful for pie chart labellingcheckPointIntersection()– for singular x/y intersection checkingcheckCircleIntersects()– checking points at several angles and radii. Returns an array of intersecting pointsgetBestMatchArr()– find best match by comparing intersection arrays' length
svg optimizations:
- reduce unnecessary background elements
- inherit properties by parent groups
You can reduce the svg data significantly by removing inner/counter shapes from the background layers (~ from 400KB to 150 KB(incliuding labels) – still not lightweight).
However, as @the Hutt already pointed out:
You should rather cache or store the retrieved label coordinates statically to avoid expensive isPointInFill() calculations every time you load your site/view.
codepen example
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 |




