Как сложить прямоугольники соответственно высоте предыдущего прямоугольника?

#javascript #d3.js #svg

#javascript #d3.js #svg

Вопрос:

Я пытаюсь сделать простой визуальный столбец, в котором прямоугольники имеют высоту, определяемую масштабом:

 var heightScale = d3.scaleLinear()
    .domain([150,2500])
    .range([10,80]);
 

Мой код выглядит так

 var margins = {top:100, left:80, bottom:40, right:20};
var width = 950;
var height = 600;
var totalWidth = width margins.left margins.right;
var totalHeight = height margins.top margins.bottom;

var svg = d3.select('body')
    .append('svg')
    .attr('width', totalWidth)
    .attr('height', totalHeight);

var graphGroup = svg.append("g")
    .attr("transform", "translate("   margins.left   ","   margins.top   ")");

var q12018Data = [{'fmc': 'UBS', 'value': 2308.78},
 {'fmc': 'Fidelity', 'value': 976.76},
 {'fmc': 'JP Morgan', 'value': 837.19},
 {'fmc': 'Value Partners', 'value': 787.39},
 {'fmc': 'BlackRock', 'value': 664.42},
 {'fmc': 'Krane', 'value': 445.13},
 {'fmc': 'Investec', 'value': 407.40},
 {'fmc': 'Nikko', 'value': 356.07},
 {'fmc': 'Yuanta', 'value': 202.71},
 {'fmc': 'Cathay Securities Invest', 'value': 174.46}];

var q22018Data = [{'fmc': 'UBS', 'value': 2193.05},
 {'fmc': 'BlackRock', 'value': 817.24},
 {'fmc': 'Yuanta', 'value': 676.87},
 {'fmc': 'Fubon', 'value': 660.11},
 {'fmc': 'JP Morgan', 'value': 577.26},
 {'fmc': 'Investec', 'value': 384.65},
 {'fmc': 'Hang Seng', 'value': 289.92},
 {'fmc': 'Cathay Securities Invest', 'value': 289.43},
 {'fmc': 'Pictet', 'value': 232.74},
 {'fmc': 'Nikko', 'value': 224.73}];

var q32018Data = [{'fmc': 'ChinaAMC', 'value': 1074},
 {'fmc': 'Fubon', 'value': 466.32},
 {'fmc': 'Heungkuk', 'value': 458.23},
 {'fmc': 'BlackRock', 'value': 361.34},
 {'fmc': 'UBS', 'value': 350.01},
 {'fmc': 'CSOP', 'value': 263.60},
 {'fmc': 'Legal amp; General', 'value': 218.79},
 {'fmc': 'SSgA', 'value': 188.35},
 {'fmc': 'Fidelity', 'value': 134.06},
 {'fmc': 'Morgan Stanley', 'value': 112.70}];

var q42018Data = [{'fmc': 'ChinaAMC', 'value': 1994.02},
 {'fmc': 'Fubon', 'value': 808.34},
 {'fmc': 'Heungkuk', 'value': 676.26},
 {'fmc': 'BlackRock', 'value': 668.64},
 {'fmc': 'UBS', 'value': 595.27},
 {'fmc': 'CSOP', 'value': 424.83},
 {'fmc': 'Legal amp; General', 'value': 380.30},
 {'fmc': 'SSgA', 'value': 366.85},
 {'fmc': 'Fidelity', 'value': 285.09},
 {'fmc': 'Morgan Stanley', 'value': 273.55}];

var q12019Data = [{'fmc': 'UBS', 'value': 938.23},
 {'fmc': 'BlackRock', 'value': 474.45},
 {'fmc': 'Yuanta', 'value': 385.32},
 {'fmc': 'Fubon', 'value': 349.73},
 {'fmc': 'JP Morgan', 'value': 246.86},
 {'fmc': 'Investec', 'value': 235.12},
 {'fmc': 'Hang Seng', 'value': 230.23},
 {'fmc': 'Cathay Securities Invest', 'value': 220.02},
 {'fmc': 'Pictet', 'value': 213.76},
 {'fmc': 'Nikko', 'value': 190.73}];


var heightScale = d3.scaleLinear()
    .domain([150,2500])
    .range([10,80]);

    var column = graphGroup.selectAll("g")
        .data(q12018Data)
      .enter().append("g");

    column.append("rect")
        .attr("width", 150)
        .attr("height", function(d) {return heightScale(d.value)})
        .attr('y', function(d,i) {
          if (i!=0) {
            var prevData = column.data()[i-1];
            var prevHeight = heightScale(prevData.value);
            var currentHeight = heightScale(d.value);
            return prevHeight;
          } else {
            return 0;
          }

        })
        .style('fill','gray');

    column.append("text")
        .attr("x", 75)
        .attr('y', function(d,i) {
          if (i!=0) {
            var prevData = column.data()[i-1];
            var prevHeight = heightScale(prevData.value);
            var currentHeight = heightScale(d.value);
            return prevHeight;
          } else {
            return 0;
          }

        })
        .attr('text-anchor','middle')
        .text(function(d) { return d.fmc; }); 
 <!DOCTYPE html>
<meta charset="utf-8">
<style>

</style>
<body>
<script src="https://d3js.org/d3.v5.min.js"></script> 

Вопрос

Почему моя высота прямой кишки по сравнению с предыдущей прямой кишкой вычисляется неправильно? Результат должен быть похож на сложенный столбик по внешнему виду. Однако масштаба y нет, я просто хочу добавить последующий прямоугольник, чтобы было всего несколько пикселей отступа, скажем, 2 пикселя.

Ответ №1:

Вы не суммируете значения! Вы просто передаете индивидуальное значение для каждого объекта… вы должны их накапливать.

Мы можем сделать это, просто объявив два счетчика с начальным значением, равным нулю…

 let counterRect = 0,
    counterText = 0;
 

… который мы позже увеличим, используя:

 .attr('y', function(d, i) {
    let previous = counterRect;
    return (counterRect  = heightScale(d.value), previous)
})
 

Вот ваш код с этим изменением (и белыми границами для прямоугольников):

 var margins = {
  top: 100,
  left: 80,
  bottom: 40,
  right: 20
};
var width = 950;
var height = 600;
var totalWidth = width   margins.left   margins.right;
var totalHeight = height   margins.top   margins.bottom;

var svg = d3.select('body')
  .append('svg')
  .attr('width', totalWidth)
  .attr('height', totalHeight);

var graphGroup = svg.append("g")
  .attr("transform", "translate("   margins.left   ","   margins.top   ")");

var q12018Data = [{
    'fmc': 'UBS',
    'value': 2308.78
  },
  {
    'fmc': 'Fidelity',
    'value': 976.76
  },
  {
    'fmc': 'JP Morgan',
    'value': 837.19
  },
  {
    'fmc': 'Value Partners',
    'value': 787.39
  },
  {
    'fmc': 'BlackRock',
    'value': 664.42
  },
  {
    'fmc': 'Krane',
    'value': 445.13
  },
  {
    'fmc': 'Investec',
    'value': 407.40
  },
  {
    'fmc': 'Nikko',
    'value': 356.07
  },
  {
    'fmc': 'Yuanta',
    'value': 202.71
  },
  {
    'fmc': 'Cathay Securities Invest',
    'value': 174.46
  }
];

var q22018Data = [{
    'fmc': 'UBS',
    'value': 2193.05
  },
  {
    'fmc': 'BlackRock',
    'value': 817.24
  },
  {
    'fmc': 'Yuanta',
    'value': 676.87
  },
  {
    'fmc': 'Fubon',
    'value': 660.11
  },
  {
    'fmc': 'JP Morgan',
    'value': 577.26
  },
  {
    'fmc': 'Investec',
    'value': 384.65
  },
  {
    'fmc': 'Hang Seng',
    'value': 289.92
  },
  {
    'fmc': 'Cathay Securities Invest',
    'value': 289.43
  },
  {
    'fmc': 'Pictet',
    'value': 232.74
  },
  {
    'fmc': 'Nikko',
    'value': 224.73
  }
];

var q32018Data = [{
    'fmc': 'ChinaAMC',
    'value': 1074
  },
  {
    'fmc': 'Fubon',
    'value': 466.32
  },
  {
    'fmc': 'Heungkuk',
    'value': 458.23
  },
  {
    'fmc': 'BlackRock',
    'value': 361.34
  },
  {
    'fmc': 'UBS',
    'value': 350.01
  },
  {
    'fmc': 'CSOP',
    'value': 263.60
  },
  {
    'fmc': 'Legal amp; General',
    'value': 218.79
  },
  {
    'fmc': 'SSgA',
    'value': 188.35
  },
  {
    'fmc': 'Fidelity',
    'value': 134.06
  },
  {
    'fmc': 'Morgan Stanley',
    'value': 112.70
  }
];

var q42018Data = [{
    'fmc': 'ChinaAMC',
    'value': 1994.02
  },
  {
    'fmc': 'Fubon',
    'value': 808.34
  },
  {
    'fmc': 'Heungkuk',
    'value': 676.26
  },
  {
    'fmc': 'BlackRock',
    'value': 668.64
  },
  {
    'fmc': 'UBS',
    'value': 595.27
  },
  {
    'fmc': 'CSOP',
    'value': 424.83
  },
  {
    'fmc': 'Legal amp; General',
    'value': 380.30
  },
  {
    'fmc': 'SSgA',
    'value': 366.85
  },
  {
    'fmc': 'Fidelity',
    'value': 285.09
  },
  {
    'fmc': 'Morgan Stanley',
    'value': 273.55
  }
];

var q12019Data = [{
    'fmc': 'UBS',
    'value': 938.23
  },
  {
    'fmc': 'BlackRock',
    'value': 474.45
  },
  {
    'fmc': 'Yuanta',
    'value': 385.32
  },
  {
    'fmc': 'Fubon',
    'value': 349.73
  },
  {
    'fmc': 'JP Morgan',
    'value': 246.86
  },
  {
    'fmc': 'Investec',
    'value': 235.12
  },
  {
    'fmc': 'Hang Seng',
    'value': 230.23
  },
  {
    'fmc': 'Cathay Securities Invest',
    'value': 220.02
  },
  {
    'fmc': 'Pictet',
    'value': 213.76
  },
  {
    'fmc': 'Nikko',
    'value': 190.73
  }
];

let counterRect = 0,
  counterText = 0;

var heightScale = d3.scaleLinear()
  .domain([150, 2500])
  .range([10, 80]);

var column = graphGroup.selectAll("g")
  .data(q12018Data)
  .enter().append("g");

column.append("rect")
  .attr("width", 150)
  .attr("height", function(d) {
    return heightScale(d.value)
  })
  .attr('y', function(d, i) {
    let previous = counterRect;
    return (counterRect  = heightScale(d.value), previous)
  })
  .style('fill', 'gray')
  .style("stroke", "white");

column.append("text")
  .attr("x", 75)
  .attr('y', function(d, i) {
    let previous = counterText;
    return (counterText  = heightScale(d.value), previous   (heightScale(d.value)/2))
  })
  .attr("dominant-baseline", "central")
  .attr('text-anchor', 'middle')
  .text(function(d) {
    return d.fmc;
  }); 
 <script src="https://d3js.org/d3.v5.min.js"></script> 

Комментарии:

1. Блестяще! Просто интересно, как передать счетчику правильное значение, чтобы текст отображался как вертикально центрированный в прямоугольнике, а не вверху.

2. Просто используйте dominant-baseline: central и добавьте половину текущего значения. Взгляните на фрагмент еще раз.