javascriptamchartsamcharts4

Linear Gauge functionality via AmChart 4


I'm looking to do a chart like this one using amcharts 4.

The question linked only supports AmCharts 3, and AmCharts 4 has no .addGraph() functionality. Is it possible to do this using XYCharts()?

const chart = new am4core.create(chartDiv.current, am4charts.XYChart);
chart.dataProvider = chartData;
chart.categoryField = 'category';
chart.rotate = true;
chart.columnWidth = 1;

// AXES
// Category
const categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
categoryAxis.gridAlpha = 0;
categoryAxis.axisAlpha = 0;
categoryAxis.gridPosition = 'start';

// value
const valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis.stackType = '100%';
valueAxis.gridAlpha = 0;
valueAxis.autoGridCount = false;
valueAxis.gridCount = 20;
valueAxis.axisAlpha = 1;

// GRAPHS
// firstgraph
const graph = new am4charts.XYChart();
graph.labelText = 'Bad';
graph.valueField = 'bad';
graph.type = 'column';
graph.lineAlpha = 0;
graph.fillAlphas = 1;
graph.fillColors = ['#d05c4f', '#ffb2a8'];
chart.createChild(graph);

I tried chart.createChild(), but that appears to be for containers like rectangles. How would I achieve the same functionality using AmCharts 4?


Solution

  • A gauge chart is essentially a stacked column(bar) chart of only 1 type of series data. I've modified stacked column chart to look like the gauge chart linked in the question.

    working demo: https://codepen.io/rabelais88/pen/RwMGxxJ

    <script src="https://cdn.amcharts.com/lib/4/core.js"></script>
    <script src="https://cdn.amcharts.com/lib/4/charts.js"></script>
    <script src="https://cdn.amcharts.com/lib/4/themes/animated.js"></script>
    <div id="chartdiv"></div>
    <style>
    #chartdiv {
      width: 100%;
      height: 400px;
    }
    </style>
    
    // Themes begin
    am4core.useTheme(am4themes_animated);
    // Themes end
    
    // Create chart instance
    var chart = am4core.create("chartdiv", am4charts.XYChart);
    
    // Add data
    chart.data = [
      {
        type: "gauge",
        bad: 2,
        good: 7,
        worst: 1
      }
    ];
    
    // Create axes
    var categoryAxis = chart.yAxes.push(new am4charts.CategoryAxis());
    categoryAxis.dataFields.category = "type";
    categoryAxis.renderer.grid.template.location = 0;
    categoryAxis.renderer.minGridDistance = 20;
    // forcefully expand axis to make it look like gauge
    categoryAxis.renderer.cellStartLocation = -0.12;
    categoryAxis.renderer.cellEndLocation = 1.12;
    categoryAxis.visible = false;
    
    var valueAxis = chart.xAxes.push(new am4charts.ValueAxis());
    // remove inner margins by syncing its start and end with min and max
    valueAxis.min = 0;
    valueAxis.max = 10;
    
    // Create series
    function createSeries(field, name, stacked) {
      var series = chart.series.push(new am4charts.ColumnSeries());
      series.dataFields.valueX = field;
      series.dataFields.categoryY = "type";
      series.name = name;
      series.columns.template.tooltipText = "{name}: [bold]{valueX[/]}";
      series.stacked = stacked;
      // add inner text
      const bullet = series.bullets.push(new am4charts.LabelBullet());
      bullet.label.text = "{name}";
      bullet.locationX = 0.5;
    }
    
    createSeries("good", "Good", false); // base of stacked column
    createSeries("bad", "Bad", true);
    createSeries("worst", "Worst", true);
    
    // Add legend
    chart.legend = new am4charts.Legend();
    

    Edit

    I've added hand as requested.

    On a side note, normally if a chart has anything unusual element, it's better to implement it with D3.js by scratch; fiddling with a pre-made chart will bring too much side effects later.

    // adding a pointing hand(clock hand) as annotation
    // draw pointing hand
    var series = chart.series.push(new am4charts.ColumnSeries());
    series.dataFields.valueX = "current";
    series.dataFields.categoryY = "type";
    series.fillOpacity = 0;
    // hide shape
    series.stroke = am4core.color("rgba(0,0,0,0)");
    // make it ignore other columns
    series.clustered = false;
    // disable tooltips
    series.interactionsEnabled = false;
    const bullet = series.bullets.push(new am4core.Triangle());
    bullet.width = 30;
    bullet.height = 30;
    bullet.fill = am4core.color("black");
    bullet.horizontalCenter = "middle";
    bullet.verticalCenter = "top";
    bullet.rotation = 180;
    // manually change its position
    bullet.dy = -65;
    
    const label = series.bullets.push(new am4charts.LabelBullet());
    label.label.text = "current: {valueX}";
    label.label.dy = -30;
    

    updated working demo with pointing hand(clock hand): https://codepen.io/rabelais88/pen/mdxOyYQ

    enter image description here