Calc / Поместите горизонтальные метки вокруг SVG Donut Graph (БЕЗ БИБЛИОТЕК) - PullRequest
0 голосов
/ 03 октября 2018

Я хочу добавить «плавающие» надписи вокруг графа SVG Donut Graph, как показано ниже, используя только элементы SVG и ванильный JS.

Соответствующие строки являются плюсом, но не обязательны.

enter image description here

Построение динамического пончика / секторов завершено, и я правильно перенес его / работаю в приложении Angular.Я использую vanilla JS w / Jquery для простоты.

Я изучил StackOverflow и Google до глубины, но не могу найти Вопрос и Ответ, который предоставляет совместимое решение с использованием алгоритма / кода для расчета желаемого размещения.90% подобных вопросов ссылаются на d3.js, в то время как другие ссылаются на другую библиотеку диаграмм.

Не уверен, с чего начать, так как я не думаю, что информации, имеющейся у меня в сегментах, достаточно для расчета размещения внутри viewBox.

Я знаю, что окружность нарисованных кругов - это каждый 100 с радиусом 15.91549430918954.stroke-dasharray и stroke-dashoffset рассчитываются и устанавливаются для каждого сегмента для правильного построения цветных сегментов.

Можно ли использовать, какие данные мне нужны, чтобы выяснить это?Мне нужно немного больше?Как бы я перевести расчет в координаты х / у для viewBox?

Спасибо.

$(document).ready(function() {
  let data = [500,100,350,600];
  let dataTotal = 0;
  for (let i = 0; i < data.length; i++) {
    dataTotal += data[i];
  }
  let colors = ['#ce4b99', '#4286f4', '#62f441', '#952ec1'];
  let labels = ['A', 'B', 'C', 'D'];
  let total = 0;
  for (let i = 0; i < data.length; i++) {
    let dataPercent = data[i] / dataTotal;
    let dataVal = 100 * dataPercent;
    var chart = document.getElementById("chart");
    const group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    const node = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
    const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
    chart.appendChild(group);
    group.appendChild(node);
    group.appendChild(label);
    label.textContent = labels[i];
    label.setAttribute('class', 'data-label');
    label.setAttribute('x', '20%');
    label.setAttribute('y', '20%');
  
    node.setAttribute('stroke-dasharray',`${dataVal} ${100 - dataVal}`);
    node.setAttribute('class','donut-segment');
    node.setAttribute('fill','transparent');
    node.setAttribute('stroke-dashoffset', getOffset(total, i))
    total += dataVal;
    node.setAttribute('stroke', colors[i]);
    node.setAttribute('stroke-width','3');
    node.setAttribute('r','15.91549430918954');
    node.setAttribute('cx','42');
    node.setAttribute('cy','42');
  }
});

function getOffset(total, i) {
  if (i === 0) return 25;
  return ((100 - total) + 25);
}
.chart-contain {
  width: 50%;
  margin 0 auto;
}
.data-label {
  font-size: 4px;
}
<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js'></script>
<div class='chart-contain'>
<svg id='chart' width="100%" height="100%" viewBox="0 0 84 84" class="donut">
  <circle class="donut-hole" cx="42" cy="42" r="15.91549430918954" fill="transparent"></circle>
  <circle class="donut-ring" cx="42" cy="42" r="15.91549430918954" fill="transparent" stroke="#d2d3d4" stroke-width="3">
  </circle>
</svg>
</div>

1 Ответ

0 голосов
/ 04 октября 2018

Вам может не понравиться мой ответ, потому что я не смог использовать ваш код.Мне нужно было организовать данные по-другому.Однако я придерживаюсь общей идеи: я использую 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, а затем повернуть текст назад.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...