如何在 google 气泡图中将轴标签与图表区域外的扩展主要网格线对齐

How to align axis label with extended major gridlines outside the chartarea in google bubble chart

我正在使用 google 可视化气泡图,我需要像下面这样对齐垂直轴标签,我想将标签对齐到图表的边距而不是轴线,还需要 2线并将主网格线延伸到图表区域之外。

代码也在这里:

<div data-ng-app="mainApp" data-ng-controller="mainSearchController"
     ng-init="ShowChart()">
    <div class="row" ng-mouseover="mousepoints($event)">
        <div google-chart chart="saleChart"
             agc-on-mouseover="showTooltip(row)"
             agc-on-mouseout="hideTooltip()">
        </div>
        <div id="custom_tooltip"
             style="position:fixed; border:0px solid #777777;
                    padding-left:10px; line-height:15px; color:#5f5f5f;
                    font-family:Arial; background-color:#FFFFFF;
                    height:auto; width:auto; font-size:10px;">
        </div>
    </div>
</div>

这是绑定图表的angularjs代码

var app = angular.module('mainApp', ['googlechart']);

app.controller('mainSearchController', function ($scope) {
    
    $scope.ShowChart = function () {     
        var saleChart = {};
        saleChart.type = 'BubbleChart';
        saleChart.cssStyle = "height:100%; width:100%;";
        var options = {
            sizeAxis: {
                maxSize: 7,
                minSize: 1
            },
            fontSize:10,
            legend: 'none',
            height: 200,
            width: 400,
            bubble: { stroke: '#fdca0f', opacity: 1 },
            colors: ['#fdca0f', '#fdca0f'],
            tooltip: {
                trigger: 'none'
            },
            hAxis: {
                ticks: [
                    { v: 800, f: '2015' },
                    { v: 1200, f: '2016' },
                    { v: 1600, f: '2017' },
                    { v: 2000, f: '2018' },
                    { v: 2400, f: '2019' },
                    { v: 2800, f: '2020' }
                ],
                gridlines: { color: '#dedede' },
                minorGridlines: { color: '#f7f7f7', count: 3 },
                textStyle: { color: '#5f5f5f' }    
            },
            vAxis: {
                ticks: [
                    { v: 1, f: 'Chennai in March' },
                    { v: 2, f: 'Mumbai in March' },
                    { v: 3, f: 'Delhi in April' },
                    { v: 4, f: 'Chennai in April' }
                    
                ],
                gridlines: { color: '#dedede' },
                textStyle: { color: '#5f5f5f' }
            }
        };
            
        var d = [
          ["Name", "Year", "Place", "", "Sales", "tooltip"],
          ["", 1000, 2, "", 26, "Sale List"],
          ["",1200,3,"",28,"Sale List"],
          ["",1400,3,"",48,"S"],
          ["",1600,3,"",29,"S"]
        ];
        saleChart.data = d;
        $scope.chartData = d;
        saleChart.options = options;
        $scope.saleChart = saleChart;        
        
    }

    var mouseX;
    var mouseY;
    $scope.mousepoints = function (e) {
        mouseX = e.pageX;
        mouseY = e.pageY;
    }

    $scope.showTooltip = function (row) {    
        var x = mouseX;
        var y = mouseY + 10;        
        if (row != null) {        
            dataTable = google.visualization.arrayToDataTable($scope.chartData);
            var v = dataTable.getValue(row, 5);
            //var v = $scope.chartData.rows[row][5];
            v = v.toString().replace(/,/g, "<br/>")

            $('#custom_tooltip').html('<div>' + v + '</div>').css({
                'top': y,
                'left': x
            }).fadeIn('slow');
        }
    }
    $scope.hideTooltip = function () {
        $('#custom_tooltip').fadeOut('fast');
    }
});

google 图表使用剪辑路径附加可视图形。您可以使用 <circle> 元素上的 cx(水平轴)、cy(垂直轴)和 r(半径)属性来控制它。 由于每一行将(或必须)具有相同的高度,您可以简单地添加一些 js 来制作 cy = cy - 10;(或任何使您的圆圈出现在您想要的位置的数字。

但是这里还有一个问题,你不能在顶轴或底轴上设置圆圈。好吧,你可以,但它将是 canvas 的一半。在这一点上我认为这里没什么可做的,不能在这个元素上使用 z-index css 属性 所以你可能无法达到所需的方法。

请求的更改只能通过手动修改图表的 SVG 来完成,
这可以在图表的 'ready' 事件上完成。

首先,将就绪事件添加到 <div google-chart> 元素...

<div google-chart chart="saleChart" agc-on-ready="onReady(chartWrapper)"
     agc-on-mouseover="showTooltip(row)" agc-on-mouseout="hideTooltip()">
</div>

然后将侦听器添加到控制器...

为了将标签向下移动,找到 <text> 个元素,
并更改其 'y' 属性。

至于网格线(<rect>),我们需要更改'x'属性,以及'width'.
不仅在网格线上,还有包含网格线的 <rect> 元素。

// ready event
$scope.onReady = function (chartWrapper) {
  // find, move labels
  var labels = chartWrapper.getChart().getContainer().getElementsByTagName('text');
  Array.prototype.forEach.call(labels, function(label) {
    if (label.getAttribute('text-anchor') === 'end') {
      var yLabel = parseFloat(label.getAttribute('y')) + (parseFloat(label.getAttribute('font-size')) * 2);
      label.setAttribute('y', yLabel);
    }
  });

  // find, expand grid lines
  var gridLines = chartWrapper.getChart().getContainer().getElementsByTagName('rect');
  Array.prototype.forEach.call(gridLines, function(line) {
    if ((line.getAttribute('height') === '1') ||
        ((line.getAttribute('x') !== '0') &&
        ((line.getAttribute('fill') === null) || (line.getAttribute('fill') === '#ffffff')))) {
      var lineWidth = parseFloat(line.getAttribute('width')) + parseFloat(line.getAttribute('x')) - 2;
      line.setAttribute('x', 2);
      line.setAttribute('width', lineWidth);
    }
  });
}

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

var app = angular.module('mainApp', ['googlechart']);

app.controller('mainSearchController', function ($scope) {
    $scope.ShowChart = function () {
        var saleChart = {};
        saleChart.type = 'BubbleChart';
        saleChart.cssStyle = "height:100%; width:100%;";
        var options = {
            sizeAxis: {
                maxSize: 7,
                minSize: 1
            },
            fontSize:10,
            legend: 'none',
            height: 200,
            width: 400,
            bubble: { stroke: '#fdca0f', opacity: 1 },
            colors: ['#fdca0f', '#fdca0f'],
            tooltip: {
                trigger: 'none'
            },
            hAxis: {
                ticks: [
                    { v: 800, f: '2015' },
                    { v: 1200, f: '2016' },
                    { v: 1600, f: '2017' },
                    { v: 2000, f: '2018' },
                    { v: 2400, f: '2019' },
                    { v: 2800, f: '2020' }
                ],
                gridlines: { color: '#dedede' },
                minorGridlines: { color: '#f7f7f7', count: 3 },
                textStyle: { color: '#5f5f5f' }
            },
            vAxis: {
                ticks: [
                    // add line break --> \n
                    { v: 1, f: 'Chennai\nin March' },
                    { v: 2, f: 'Mumbai\nin March' },
                    { v: 3, f: 'Delhi\nin April' },
                    { v: 4, f: 'Chennai\nin April' }
                ],
                gridlines: { color: '#dedede' },
                textStyle: { color: '#5f5f5f' }
            }
        };

        var d = [["Name", "Year", "Place", "", "Sales", "tooltip"],
        ["", 1000, 2, "", 26, "Sale List"],
        ["",1200,3,"",28,"Sale List"],
        ["",1400,3,"",48,"S"],["",1600,3,"",29,"S"]];
        saleChart.data = d;
        $scope.chartData = d;
        saleChart.options = options;
        $scope.saleChart = saleChart;

    }

    var mouseX;
    var mouseY;
    $scope.mousepoints = function (e) {
        mouseX = e.pageX;
        mouseY = e.pageY;
    }

    $scope.showTooltip = function (row) {
        var x = mouseX;
        var y = mouseY + 10;
        if (row != null) {
            dataTable = google.visualization.arrayToDataTable($scope.chartData);
            var v = dataTable.getValue(row, 5);
            //var v = $scope.chartData.rows[row][5];
            v = v.toString().replace(/,/g, "<br/>")

            $('#custom_tooltip').html('<div>' + v + '</div>').css({
                'top': y,
                'left': x
            }).fadeIn('slow');
        }
    }
    $scope.hideTooltip = function () {
        $('#custom_tooltip').fadeOut('fast');
    }
    
    $scope.onReady = function (chartWrapper) {
      var labels = chartWrapper.getChart().getContainer().getElementsByTagName('text');
      var labelIndex = 0;
      var nextLabels = [];
      Array.prototype.forEach.call(labels, function(label) {
        // find label
        if (label.getAttribute('text-anchor') === 'end') {
          // move label down
          var yLabel = parseFloat(label.getAttribute('y')) + (parseFloat(label.getAttribute('font-size')) * 1.5);
          label.setAttribute('y', yLabel);
          
          // set text line 1
          var labelText = chartWrapper.getOption('vAxis.ticks')[labelIndex].f.split('\n');
          label.textContent = labelText[0].toUpperCase();
          
          // save label
          nextLabels.push(label);
          labelIndex++;
        }
      });
      
      // add line 2
      nextLabels.forEach(function (label, labelIndex) {
        var yLabel = parseFloat(label.getAttribute('y')) + (parseFloat(label.getAttribute('font-size')) + 1);
        var nextLabel = label.parentNode.appendChild(label.cloneNode(true)); 
        var labelText = chartWrapper.getOption('vAxis.ticks')[labelIndex].f.split('\n');
        nextLabel.textContent = labelText[1];
        nextLabel.setAttribute('y', yLabel);
        
        // increase font size of line 1
        label.setAttribute('font-size', (parseFloat(label.getAttribute('font-size')) + 1));
        
        // re-align labels to left
        var labelWidth = label.getBBox().width;
        label.setAttribute('x', labelWidth + 2);
        labelWidth = nextLabel.getBBox().width;
        nextLabel.setAttribute('x', labelWidth + 2);
      });


      var gridLines = chartWrapper.getChart().getContainer().getElementsByTagName('rect');
      Array.prototype.forEach.call(gridLines, function(line) {
        if ((line.getAttribute('height') === '1') ||
            ((line.getAttribute('x') !== '0') &&
            ((line.getAttribute('fill') === null) || (line.getAttribute('fill') === '#ffffff')))) {
          var lineWidth = parseFloat(line.getAttribute('width')) + parseFloat(line.getAttribute('x')) - 2;
          line.setAttribute('x', 2);
          line.setAttribute('width', lineWidth);
        }
      });
    }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.8/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-google-chart/0.1.0/ng-google-chart.min.js"></script>

<div data-ng-app="mainApp" data-ng-controller="mainSearchController" ng-init="ShowChart()">
  <div class="row" ng-mouseover="mousepoints($event)">
    <div google-chart chart="saleChart" agc-on-mouseover="showTooltip(row)" agc-on-mouseout="hideTooltip()" agc-on-ready="onReady(chartWrapper)"></div>
    <div id="custom_tooltip" style="position:fixed; border:0px solid #777777; padding-left:10px; line-height:15px; color:#5f5f5f; font-family:Arial; background-color:#FFFFFF; height:auto; width:auto; font-size:10px;"></div>
  </div>
</div>