如何使用 Google 图表在条形图顶部插入点?

How to insert points on top of bar charts using Google Charts?

鉴于插图

我能够使用以下代码创建图表(没有点):

<html>
  <head>
      <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
    <script type="text/javascript">
      google.charts.load('current', {'packages':['bar']});
      google.charts.setOnLoadCallback(drawStuff);

      var chart;

      function drawStuff() {
        var data = new google.visualization.arrayToDataTable([
          ['Y', 'Series 1','Series 2'],
          ["Element A", 44,11],
          ["Element B", 31,11],
          ["Element C", 12,11],
        ]);

        var options = {
          bars: 'horizontal', // Required for Material Bar Charts.
          axes: {
            x: {
              0: { side: 'bottom', label: 'X'} // Top x-axis.
            }
          }
        };

        var div = document.getElementById('top_x_div');
        var link = document.getElementById('url');
        chart = new google.charts.Bar(div);
        chart.draw(data, options);
        //console.log(chart.getImageURI());
      };
    </script>
  </head>
  <body>
    <div id="top_x_div" style="width: 900px; height: 500px;"></div>
  </body>
</html>

如何使用 Google 图表在条形图顶部插入点?

我想到了几种方法来完成...

  1. 使用组合图表
  2. 在图表的 'ready' 事件
  3. 上手动添加点

然而,使用 material 图表无法实现这两个选项。

google.charts.Bar

首先,material图表不支持组合图表。
并且,material 图表没有手动添加点所需的 methods

更不用说,还有几个material图表不支持的选项
参见 --> Tracking Issue for Material Chart Feature Parity #2143

所以我们必须使用经典图表...

google.visualization.BarChart

我们可以使用一个选项为 经典 图表提供 相似 的外观和感觉 material 图表...

theme: 'material'

现在解决方案...

尝试使用组合图表时,
这些点与 y 轴对齐,位于两个条形之间。
没有一个选项可以让这些点 条上对齐。
所以我们必须在图表的 'ready' 事件中手动添加点...

我们需要的图表很少methods...

getChartLayoutInterface() - Returns an object containing information about the onscreen placement of the chart and its elements.

getBoundingBox(id) - Returns an object containing the left, top, width, and height of chart element id.

getXLocation(position, optional_axis_index) - Returns the screen x-coordinate of position relative to the chart's container.

图表的 'ready' 事件触发后,我们可以使用 --> getChartLayoutInterface()

获取布局界面

上面提到的另外两个方法是布局接口的方法

我们使用 getBoundingBox(id) 获取柱的坐标,点将放置在该柱上。
这将为我们提供 Y 坐标和点的高度。
柱的id由数据行和系列列决定。

bar#C#R  // where C is the series column index, and R is the row index

我们正在提供 X 坐标,
getXLocation 将提供图表上的位置,需要将点放在我们预定的 x 轴上。

// x coordinates of points to add
var points = [
  [36, 8],
  [28, 8],
  [10, 8],
];

// loop data rows and columns
for (var r = 0; r < data.getNumberOfRows(); r++) {
  for (var c = 1; c < data.getNumberOfColumns(); c++) {
    // calculate position of the point
    var barBounds = chartLayout.getBoundingBox('bar#' + (c - 1) + '#' + r);
    var xCoord = chartLayout.getXLocation(points[r][c - 1]);
    var height = barBounds.height / 4;
    var top = barBounds.top + (height * 2);

    // create and add the point to the chart
    var point = document.createElementNS(svgNS, 'circle');
    point.setAttribute('cx', xCoord);
    point.setAttribute('cy', top);
    point.setAttribute('r', height);
    point.setAttribute('stroke', '#ffffff');
    point.setAttribute('stroke-width', height);
    point.setAttribute('fill', 'transparent');
    svg.appendChild(point);
  }
}

请参阅以下工作片段...

var chart;

google.charts.load('current', {
  packages: ['corechart']
}).then(function drawStuff() {
  var data = new google.visualization.arrayToDataTable([
    ['Y', 'Series 1', 'Series 2'],
    ['Element A', 44, 11],
    ['Element B', 31, 11],
    ['Element C', 12, 11],
  ]);

  // x coordinates of points to add
  var points = [
    [36, 8],
    [28, 8],
    [10, 8],
  ];

  var options = {
    chartArea: {
      left: 128,
      top: 24,
      right: 128,
      bottom: 72,
      height: '100%',
      width: '100%'
    },
    hAxis: {
      title: 'X'
    },
    height: '100%',
    theme: 'material',
    vAxis: {
      title: data.getColumnLabel(0)
    },
    width: '100%'
  };

  var div = document.getElementById('top_x_div');
  //var link = document.getElementById('url');

  chart = new google.visualization.BarChart(div);

  google.visualization.events.addListener(chart, 'ready', function () {
    // get chart layour interface and svg
    var chartLayout = chart.getChartLayoutInterface();
    var svg = div.getElementsByTagName('svg')[0];
    var svgNS = svg.namespaceURI;

    // loop data rows and columns
    for (var r = 0; r < data.getNumberOfRows(); r++) {
      for (var c = 1; c < data.getNumberOfColumns(); c++) {
        // calculate position of the point
        var barBounds = chartLayout.getBoundingBox('bar#' + (c - 1) + '#' + r);
        var xCoord = chartLayout.getXLocation(points[r][c - 1]);
        var height = barBounds.height / 4;
        var top = barBounds.top + (height * 2);

        // create and add the point to the chart
        var point = document.createElementNS(svgNS, 'circle');
        point.setAttribute('cx', xCoord);
        point.setAttribute('cy', top);
        point.setAttribute('r', height);
        point.setAttribute('stroke', '#ffffff');
        point.setAttribute('stroke-width', height);
        point.setAttribute('fill', 'transparent');
        svg.appendChild(point);
      }
    }
  });

  chart.draw(data, options);
  window.addEventListener('resize', function () {
    chart.draw(data, options);
  });
});
#top_x_div {
  height: 400px;
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="top_x_div"></div>