Вам может не понравиться мой ответ, потому что я не смог использовать ваш код.Мне нужно было организовать данные по-другому.Однако я придерживаюсь общей идеи: я использую stroke-dasharray
и stroke-dashoffset
.Я бы использовал пути вместо штрихов.
Я использую радиус 20, но вы можете изменить его обратно.Вы также можете изменить его на что-нибудь еще.
Чтобы нарисовать текст, я рассчитал начальный угол и конечный угол ваших дуг.Затем я рассчитал средний угол.Как только я узнаю средний угол, я смогу нарисовать текст и линии.
Я помещаю дуги в группу #chart
, текст в группу #text
и строки в группу #lines.
const SVG_NS = 'http://www.w3.org/2000/svg';
const XLINK = 'http://www.w3.org/1999/xlink';
let r = 20,cx=42,cy=42;// the attributes for the circle
let text_r = r + 10; // the radius for the text
let perimeter = 2*r*Math.PI;//the perimeter of the circle
donut.setAttributeNS(null, "r", r);
donut.setAttributeNS(null, "cx", cx);
donut.setAttributeNS(null, "cy", cy);
let data = [
{
value:500,
stroke:'#ce4b99',
label: 'A'
},
{
value:100,
stroke:'#4286f4',
label: 'B'
},
{
value:350,
stroke:'#62f441',
label: 'C'
},
{
value:600,
stroke:'#952ec1',
label: 'D'
}
]
let total = 0;
data.map(d =>{
d.temp = total;
total += d.value;
})
data.map(d =>{
d.offset = map(d.temp,0,total,0, perimeter)
d.real_value = map(d.value, 0, total, 0, perimeter);
d.dashArray = `${d.real_value},${perimeter}`;
/// angles
let angleStart = -d.offset/r;
let angleEnd = (d.offset + d.real_value)/r;
d.angleMiddle = (angleEnd - angleStart)/2;
// text
let t = {}
t.props ={
x : cx + text_r*Math.cos(d.angleMiddle),
y : cy + text_r*Math.sin(d.angleMiddle),
}
t.textContent = d.label;
d.text_point = t;
//line
let l = {
x1 : cx + r*Math.cos(d.angleMiddle),
y1 : cy + r*Math.sin(d.angleMiddle),
x2 : cx + .9*text_r*Math.cos(d.angleMiddle),
y2 : cy + .9*text_r*Math.sin(d.angleMiddle),
}
d.line = l;
})
data.map(d=>{// create a new use element
d.use = document.createElementNS(SVG_NS, 'use');
d.use.setAttributeNS(XLINK, 'xlink:href', '#donut');
d.use.setAttributeNS(null, 'stroke', d.stroke);
d.use.setAttributeNS(null, 'stroke-dasharray', d.dashArray);
d.use.setAttributeNS(null, 'stroke-dashoffset', -d.offset);
chart.appendChild(d.use);
drawLine(d.line, lines);
drawText(d.text_point, text);
})
// helpers
function drawLine(o, parent) {
var line = document.createElementNS(SVG_NS, 'line');
for (var name in o) {
if (o.hasOwnProperty(name)) {
line.setAttributeNS(null, name, o[name]);
}
}
parent.appendChild(line);
return line;
}
function drawText(o, parent) {
var text = document.createElementNS(SVG_NS, 'text');
for (var name in o.props) {
if (o.props.hasOwnProperty(name)) {
text.setAttributeNS(null, name, o.props[name]);
}
text.textContent = o.textContent;
}
parent.appendChild(text);
return text;
}
function map(n, a, b, _a, _b) {
let d = b - a;
let _d = _b - _a;
let u = _d / d;
return _a + n * u;
}
svg{border:1px solid;}
#donut{fill:none; stroke-width:5px;}
text{dominant-baseline:central;text-anchor:middle;font-size: 4px;}
line{stroke:black;stroke-width:.1px}
<div class='chart-contain'>
<svg viewBox="0 0 84 84" width="250" class="donut">
<defs>
<circle id="donut" cx="42" cy="42" r="20" ></circle>
</defs>
<g id='text'></g>
<g id='lines'></g>
<g id='chart'></g>
</svg>
</div>
Чтобы получить его точно так же, как у вас, вы можете повернуть весь график -Math.PI / 2, а затем повернуть текст назад.