1
votes

I have to make a chart like in the picture and it looks almost like Material bar chart from google but it needs a little bit of modifications. So far I haven't find a way to modify the color on hover , the ticks number or number of lines and the width of chart area . Can someone help me ?

google.charts.load('current', {'packages':['corechart','bar']});
google.charts.setOnLoadCallback(barChart);
function barChart() {
  var data = new google.visualization.arrayToDataTable([
    ['Opening Move', 'Percentage', { role: 'style' }],
    ["King's pawn (e4)", 44, ' ' ],
    ["Queen's pawn (d4)", 31, ''],
    ["Knight to King 3 (Nf3)", 12,''],
    ["Queen's bishop pawn (c4)", 10,''],
    ['Other', 3, '']
  ]);

  var options = {
    width: 520,
    height: 320,
    legend: { 
      position: 'none' 
      },
    bars: 'horizontal', 
    axes: {
      x: {
        0: { 
          side: 'top',
          label: 'Percentage',
        } 
      }
    },
    hAxis: { ticks: [10,20,30] },
    bar: { groupWidth: "90%" },
//    chartArea: {
//      width: '78%',
//      left: '22%'
//    },
    colors:['#c6dbfd','#c6dbfd','#c6dbfd','#c6dbfd','#c6dbfd','#c6dbfd']
  };

    var chart = new google.charts.Bar(document.getElementById('bar-chart'));
    chart.draw(data, options);
  }
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="bar-chart"></div>

Chart design

2
see the list of features that don't work in Material charts, it includes most of what you need. maybe use a Core chart instead, with the option --> theme: 'material' -- some of the options may work, if you use --> google.charts.Bar.convertOptions(options)WhiteHat

2 Answers

3
votes

You are using Material Bar Charts, that have for now a very limited set of features (as indicated here and here).

This explains why ticks & chart area are not working. I have created a fiddle with a working, non Material version: https://jsfiddle.net/5Lnbbbbw/1/.

It is similar, but uses:

var chart = new google.visualization.BarChart(document.getElementById('bar-chart'));

instead of

var chart = new google.charts.Bar(document.getElementById('bar-chart'));

Now for hover: this is difficult, as there is no options that manages it. In fact, what you see on hover & on click are new elements added to the chart; it is not managed via css.

However, you can do this:

#bar-chart svg g[clip-path] g:not(:first-child) rect:hover {
    fill: blue;
    stroke: black;
    stroke-width: 1;
}

The chart area is the svg element g that has a clip-path attribute. The first child groups all the tick bars (the vertical lines), so we do not want to add a css property for them.

Please note that if you remove the ticks bars, you will probably have to update the css slightly.

If you want to know more about the css options you have for bars, you can find more info here.

0
votes

I found this codepen that adds a percentage to the bars on a chart using jQuery. Maybe it will be helpful?

http://codepen.io/sharkers/pen/qmrjVe

$(document).ready(function() {
startProdChart(); });

function startProdChart() {
    google.charts.load("current", { packages: ["bar"] });
    google.charts.setOnLoadCallback(drawProdChart);
}

function drawProdChart() {
    //create the main array that we need to fill up
    var prodChart = [];

//create the initial header array and populate with values
var header = ["Month", "Finance Reserve"]; //the static pieces
var prodDiv = $('.the-record[data-type="totals"][data-record="store totals"] .product-labels');
prodDiv.each(function() {
    var prodName = $(this).find(".header-labels .record-label").text();
    header.push(prodName);
});

//push header info into array
prodChart.push(header);

//collect data from each monthly record
var chartData = $('.the-record[data-record="store totals"][data-type="totals"] .record-column span[data-label="income"]');
chartData.each(function() {
    var tempProd = [];
    var chartDate = $(this).parent().data("date"),
        chartXLabels = $(this)
            .closest(".vehicle-labels")
            .find('.header-labels .record-label[data-date="' + chartDate + '"]')
            .text(),
        chartDataTotalIncome = parseInt($(this).text().replace(/\$|,/g, "")),
        chartFinanceTotal = parseInt(
            $(this)
                .closest(".record-holder")
                .find(
                    '.finance-labels .record-column[data-date="' +
                        chartDate +
                        '"] span[data-label="financeamt"]'
                )
                .text()
                .replace(/\$|,/g, "")
        ),
        chartFinancePerc = chartFinanceTotal / chartDataTotalIncome,
        chartProducts = $(this)
            .closest(".record-holder")
            .find(
                '.product-labels .record-column[data-date="' + chartDate + '"] span.amt'
            );

    //if untitled, label 'Averages'
    if (chartXLabels == "") {
        chartXLabels = "Average";
    }

    //push labels and finance numbers in array
    tempProd.push(chartXLabels);
    tempProd.push(chartFinanceTotal);

    //push product totals into array one by one
    chartProducts.each(function() {
        var chartProductsTotal = parseInt($(this).text().replace(/\$|,/g, "")),
            chartProductsPerc =
                parseInt($(this).text().replace(/\$|,/g, "")) / chartDataTotalIncome;
        tempProd.push(chartProductsTotal);
    });

    prodChart.push(tempProd);
});

var data = google.visualization.arrayToDataTable(prodChart);

//set chart options
var options = {
    chart: {
        title: "Total Income Percentage",
        subtitle: "Products and Finance Reserve"
    },
    bars: "vertical",
    vAxis: {
        format: "$#,###",
        textPosition: "inside"
    },
    height: 700,
    series: {
        0: {
            color: "#3eb42f"
        },
        1: {
            color: "#2264ae"
        }
    },
    isStacked: "true"
};

var chart = new google.charts.Bar(
    document.getElementById("monthly-income-chart")
);

chart.draw(data, google.charts.Bar.convertOptions(options));
google.visualization.events.addListener(chart, "ready", drawMoInc); //run the addPercs function after chart ready

function drawMoInc() {
    addPercs("monthly-income-chart");
}

}

// Pls ckeck this code

//this is a function that will add percentages within the sections of a stacked bar chart
function addPercs(chartId) {
//google material charts have two elements that make up one bar, potentially several 'rect' elements and one 'path', so we use paths to get the number of bars
var thisChart = $("#" + chartId),
    gLast = thisChart.find("svg g:last"), //the last g is where the tooltip shows up, need to insert our new text BEFORE this element
    barPath = thisChart.find("svg g:eq(2) path"), //the third g element is where the paths reside
    pathAmt = barPath.length, //the number of paths will tell us how many bars we have in the chart
    barRect = thisChart.find("svg g:eq(2) rect"), //the third g element is where the rects reside
    rectAmt = barRect.length,
    rectGroup = rectAmt / pathAmt, //find out how many rect sections are in one bar
    rectSelect = 0;

console.log(thisChart.attr("id"));

//create an empty 'g' element, append to the 'svg' element, give it a class, shift it up one so it's second to last
var newTextSvg = document.createElementNS("http://www.w3.org/2000/svg", "g");

thisChart.find("svg").append(newTextSvg);
newTextSvg.setAttribute("class", "newtext");
thisChart.find(".newtext").insertBefore(gLast);

//get the parts of the bar and create percentages
barPath.each(function() {
    var totalRectHeight = 0,
        thisGroup = rectSelect * rectGroup,
        thisSet = rectSelect * rectGroup;

    //get total height of all rects in a single bar
    for (i = 0; i < rectGroup; i++) {
        var thisRectHeight = barRect[thisGroup].getBBox().height;

        totalRectHeight = thisRectHeight + totalRectHeight;
        thisGroup++;
    }

    //get path info
    var pathHeight = barPath[rectSelect].getBBox().height,
        pathWidth = barPath[rectSelect].getBBox().width,
        pathPosX = barPath[rectSelect].getBBox().x,
        pathPosY = barPath[rectSelect].getBBox().y;

    //do some math real quick like
    var totalHeight = pathHeight + totalRectHeight,
        pathPerc = Math.round(pathHeight / totalHeight * 100);

    //get all the rect info within the bar
    for (i = 0; i < rectGroup; i++) {
        var rectHeight = barRect[thisSet].getBBox().height,
            rectWidth = barRect[thisSet].getBBox().width,
            rectPosX = barRect[thisSet].getBBox().x,
            rectPosY = barRect[thisSet].getBBox().y,
            rectPerc = Math.round(rectHeight / totalHeight * 100),
            rectText = document.createTextNode(rectPerc + "%");

        //create new svg text elements for the rect parts of the chart
        if (rectHeight !== 0) {
            var newRectText = document.createElementNS(
                "http://www.w3.org/2000/svg",
                "text"
            );
            newRectText.appendChild(rectText);
            newTextSvg.appendChild(newRectText);
            newRectText.setAttribute("x", rectPosX);
            newRectText.setAttribute("y", rectPosY);
            newRectText.setAttribute(
                "style",
                "cursor: default; user-select: none; -webkit-font-smoothing: antialiased;"
            );
            newRectText.setAttribute("fill", "#ffffff");
            var rectTextWidth = newRectText.getBBox().width,
                rectTextHeight = newRectText.getBBox().height,
                rectOffsetX = rectWidth / 2 - rectTextWidth / 2,
                rectOffsetY = 24;
            if (rectTextHeight + rectOffsetY * 2 >= rectHeight) {
                rectOffsetY = rectHeight / 2 + rectTextHeight / 3.5;
            }

            newRectText.setAttribute("dx", rectOffsetX);
            newRectText.setAttribute("dy", rectOffsetY);
        }
        thisSet++;
    }

    //path vars
    var pathText = document.createTextNode(pathPerc + "%");

    //create a new svg text element for the path parts of the chart
    var newPathText = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "text"
    );
    newPathText.appendChild(pathText);
    newTextSvg.appendChild(newPathText);
    newPathText.setAttribute("x", pathPosX);
    newPathText.setAttribute("y", pathPosY);
    newPathText.setAttribute(
        "style",
        "cursor: default; user-select: none; -webkit-font-smoothing: antialiased;"
    );
    newPathText.setAttribute("fill", "#ffffff");

    //get numbers to set the text position
    var pathTextWidth = newPathText.getBBox().width,
        pathTextHeight = newPathText.getBBox().height,
        pathOffsetX = pathWidth / 2 - pathTextWidth / 2,
        pathOffsetY = 24;
    if (pathTextHeight + pathOffsetY * 2 >= pathHeight) {
        pathOffsetY = pathHeight / 2 + pathTextHeight / 3.5;
    }

    newPathText.setAttribute("dx", pathOffsetX);
    newPathText.setAttribute("dy", pathOffsetY);

    rectSelect++; //go up one
});

}