Позиционирование треугольника D3 в точной точке - PullRequest
0 голосов
/ 31 августа 2018

Хорошее изображение Плохое изображение

Я пытаюсь создать график D3, который выглядит как дизайн, созданный Illustrator (Хорошее изображение 1), но наиболее близким с точки зрения позиционирования, который мне удалось получить, является второй (плохое изображение 2).

Я действительно новичок в D3 и вообще создаю SVG, так что я могу ошибаться. Код ниже - это то, что я смог выяснить / найти онлайн. Похоже, я не могу напрямую настроить позиционирование самих элементов с помощью позиционирования CSS? Я попытался добавить классы через HTML, а также в JQuery с $ (. MyClass) .css ..., но все, что я делаю, имеет абсолютно нулевой эффект. Единственная вещь, которая, кажется, работает - это трансформация, но она ужасна, как видно на втором рисунке.

var margin = { left:10, right:10, top:10, bottom:10 };

var width = 400 - margin.left - margin.right,
    height = 450 - margin.top - margin.bottom;

var g = d3.select("#pyramid-chart-area")
    .append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
    .append("g")
        .attr("transform", "translate(" + margin.left 
            + ", " + margin.top + ")");

d3.json("../data/pyramid_hp.json").then(function(data){
    data.forEach(function(d){
        d.hp = +d.hp;
    });

    var x = d3.scaleBand()
        .domain(data.map(function(d){ return d.hp; }))
        .range([0, width])
        .paddingInner(0.3)
        .paddingOuter(0.3);

    var y = d3.scaleLinear()
        .domain([0, d3.max(data, function(d){
            return d.hp;
        })])
        .range([height, 0]);

    var xAxisCall = d3.axisBottom(x);
    g.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0, " + height + ")")
        .call(xAxisCall)
        .selectAll("text")
            .attr("y", "10")
            .attr("x", "-5")
            .attr("text-anchor", "end")
            .attr("transform", "rotate(-40)");

    var yAxisCall = d3.axisLeft(y)
        .ticks(3)
        .tickFormat(function(d){
            return d;
        });


    g.append("g")
        .attr("class", "y-axis")
        .call(yAxisCall);


    var arc = d3.symbol().type(d3.symbolTriangle)
                .size(function(d){ return scale(d.hp); });

    var scale = d3.scaleLinear()
        .domain([0, 5])
        .range([0, width]);

    var colors = d3.scaleLinear()
    .domain(d3.extent(data, function(d) {return d.hp}))
    .range([
        '#ffffff',        
        '#303030'        
    ]);

    var group = g.append('g')
                .attr('transform','translate('+ 192 +','+ 320 +')')
                .attr('class', 'triangle-container');


    var line = group.selectAll('path')
            .data(data)
            .enter()
            .append('path')
            .attr('d', arc)
            // .attr('fill',function(d){ return colorscale(d.hp); })
            .attr('fill', d => colors(d.hp))
            .attr('stroke','#000')
            .attr('stroke-width', 1)
            .attr('class', 'triangle')
            .attr('transform',function(d,i){ return "translate("+ (i * 20) +","+(i * 10)+")"; });

1 Ответ

0 голосов
/ 31 августа 2018

Вы можете расположить символы, но их хитрый - размер символа представляет область и, поскольку rioV8 отмечает символы расположены по их центру. Но если вы можете выяснить свойства треугольника, мы можем относительно легко разместить его.

В случае равностороннего треугольника вы захотите узнать длину данной стороны, а также высоту этого центроида (которая равна высоте треугольника / 3). Так что эти функции, вероятно, будут полезны:

// get length of a side/width of upright equilateral triangle from area:
function getWidth(a) {
    return Math.sqrt(4 * a / Math.sqrt(3));
}
// get height of the triangle from length of a side
function getHeight(l) {
    return Math.sqrt(3)*l/2;
}

Используя высоту центроида, мы можем расположить окружность, где мы хотим, с чем-то вроде:

y = SVGheight - SymbolHeight/3 - marginBottom;

Нет необходимости масштабировать здесь.

Значения x каждого символа нуждаются в некотором масштабировании, чтобы расположить их по своему вкусу. Ниже я использую линейную шкалу с произвольным диапазоном [width/10,0], знаменатель изменит горизонтальный перекос в этом случае, возможно, есть более эффективные способы точной настройки .

С этим вы можете достичь желаемого результата:

enter image description here

Для простоты ниже я использую данные (поскольку вы не показываете их), представляющие область пикселей - масштабирование должно учитываться при расчете высоты и ширины, если масштабирование областей . Я также включил круги в верхней части каждого треугольника для возможного использования меток, так как мы знаем размеры треугольника, теперь это тривиально

var margin = { left:10, right:10, top:10, bottom:10 };

var width = 400 - margin.left - margin.right,
    height = 300 - margin.top - margin.bottom;

var g = d3.select("body")
  .append("svg")
     .attr("width", width + margin.left + margin.right)
     .attr("height", height + margin.top + margin.bottom)
  .append("g")
     .attr("transform", "translate(" + margin.left + ", " + margin.top + ")")


var data = [
	{a: 40000},
	{a: 30000},
	{a: 20000},
	{a: 10000}
];

function getWidth(a) {
	return Math.sqrt(4 * a / Math.sqrt(3));
}
function getHeight(l) {
	return Math.sqrt(3)*l/2;
}

data.forEach(function(d) {
	d.w = getWidth(d.a);
	d.h = getHeight(d.w);
})

var x = d3.scaleLinear()
  .domain(d3.extent(data, function(d){ return d.w; }) )
  .range([width/10,0]);


var arc = d3.symbol().type(d3.symbolTriangle)
   .size(function(d){ return d.a; });

var colors = d3.scaleLinear()
 .domain(d3.extent(data, function(d) {return d.a}))
 .range(['#ffffff','#303030']);

var group = g.append('g')
 .attr('transform','translate('+ width/2 +',0)') 
 .attr('class', 'triangle-container');

var line = group.selectAll('path')
  .data(data)
  .enter()
  .append('path')
  .attr('d', arc)
  .attr('fill', d => colors(d.a))
  .attr('class', 'triangle')
  .attr('transform',function(d,i){ return "translate("+ x(d.w) +","+ (height - d.h/3 - margin.bottom )  +")"; });

var circles =  group.selectAll("circle")
  .data(data)
  .enter()
  .append("circle")
  .attr("cx", function(d) { return x(d.w); })
  .attr("cy", function(d) { return height - d.h - margin.bottom; })
  .attr("r", 3);
 <script src='https://d3js.org/d3.v5.min.js' type='text/javascript'></script>

Оси могут быть немного сложной задачей

...