Amcharts 使用子弹图按值排序堆栈

Amcharts Sort stackbyvalue with bullet chart

我正在使用 amcharts 的 stackByValue 来排列堆叠柱形图。我想在每个图表上添加一个要点,以检查它们是否达到某个目标。目前发生的事情是将项目符号点添加到堆叠图表中,有没有一种方法可以在不删除 stackByValue 的情况下执行此操作?

这是我的 JsFiddle:`http://jsfiddle.net/sky5rvdz/13/

$(document).ready(function() {
    AmCharts.addInitHandler(function(chart) {
        // Check if enabled
        if (chart.valueAxes === undefined || chart.valueAxes.length === 0 || !chart.valueAxes[0].stackByValue)
            return;

        // Disable built-in stacking
        chart.valueAxes[0].stackType = "none";

        // Prepare all graphs
        for (var i = 0; i < chart.graphs.length; i++) {
            var graph = chart.graphs[i];
            graph.originalValueField = graph.valueField;
            graph.valueField = graph.originalValueField + "Close";
            graph.openField = graph.originalValueField + "Open";
            graph.clustered = false;
            if (graph.labelText)
                graph.labelText = graph.labelText.split("[[value]]").join("[[" + graph.originalValueField + "]]");
            if (graph.balloonText)
                graph.balloonText = graph.balloonText.split("[[value]]").join("[[" + graph.originalValueField + "]]");
        }

        // Go through each category and order values
        for (var i = 0; i < chart.dataProvider.length; i++) {

            // Assemble intermediate array of data point items
            var dp = chart.dataProvider[i];
            var items = [];
            var sum = 0;
            for (var x = 0; x < chart.graphs.length; x++) {
                var graph = chart.graphs[x];
                items.push({
                    "graph": graph,
                    "value": dp[graph.originalValueField]
                });
            }

            var sortValue = 0;

            // Order according to value
            items.sort(function(a, b) {
                if (sortValue == 0) {

                    return a.value - b.value;
                } else {

                    return b.value - a.value;
                }

            });

            // Calculate open and close fields
            var offset = 0;
            for (var x = 0; x < items.length; x++) {
                var item = items[x];
                dp[item.graph.openField] = offset;
                dp[item.graph.valueField] = offset + dp[item.graph.originalValueField];
                offset = dp[item.graph.valueField];
            }
        }
    }, ["serial"]);

    var response = [{
        "name": "Jan",
        "target": 2062186.74,
        "USA": 0,
        "MAN": 605873.95,
        "PAN": 759763.5
    }, {
        "name": "Feb",
        "target": 1492210.81,
        "MAN": 499538.43,
        "PAN": 559504.95,
        "USA": 5850
    }, {
        "name": "Mar",
        "target": 1455750,
        "MAN": 403715.2,
        "PAN": 694353.95,
        "USA": 0
    }, {
        "name": "Apr",
        "target": 2008623.96,
        "USA": 0,
        "MAN": 409993.3,
        "PAN": 511030
    }];

    var graphs = Object.keys(response[0]).reduce(function(graphsArray, key) {
        if (key !== "name" && key !== "target") {
            graphsArray.push({
                "balloonText": "<b>[[value]]</b>",
                "balloonFunction": function(item, graph) {
                    var result = graph.balloonText;
                    for (var key in item.dataContext) {
                        if (item.dataContext.hasOwnProperty(key) && !isNaN(item.dataContext[key])) {
                            var formatted = AmCharts.formatNumber(item.dataContext[key], {
                                precision: chart.precision,
                                decimalSeparator: chart.decimalSeparator,
                                thousandsSeparator: chart.thousandsSeparator
                            }, 2);
                            result = result.replace("[[" + key + "]]", formatted);
                        }
                    }
                    return result;
                },
                "fillAlphas": 0.8,
                "labelText": "[[title]]<br>",
                "labelPosition": "middle",
                "lineAlpha": 0.3,
                "title": key,
                "type": "column",
                "color": "#000000",
                //"showAllValueLabels": true,
                "valueField": key
            });
        }
        if (key === "target") {
            graphsArray.push({
                "balloonText": "<b>[[value]]</b>",
                "balloonFunction": function(item, graph) {
                    var result = graph.balloonText;
                    for (var key in item.dataContext) {
                        if (item.dataContext.hasOwnProperty(key) && !isNaN(item.dataContext[key])) {
                            var formatted = AmCharts.formatNumber(item.dataContext[key], {
                                precision: chart.precision,
                                decimalSeparator: chart.decimalSeparator,
                                thousandsSeparator: chart.thousandsSeparator
                            }, 2);
                            result = result.replace("[[" + key + "]]", formatted);
                        }
                    }
                    return result;
                },
                "valueAxis": "v2",
                "lineAlpha": 0,
                "bullet": "round",
                "bulletSize": 20,
                "title": "target",
                "type": "line",

                "valueField": "target"
            });
        }
        return graphsArray;
    }, []);

    var chart = AmCharts.makeChart("chartdiv", {
        "type": "serial",
        "theme": "light",
        "legend": {
            "horizontalGap": 10,
            "maxColumns": 1,
            "position": "right",
            "useGraphSettings": true,
            "markerSize": 10
        },
        "numberFormatter": {
            "precision": 1,
            "decimalSeparator": ".",
            "thousandsSeparator": ","
        },
        "dataProvider": response,
        "valueAxes": [{
            "id": "v1",
            "stackType": "regular",
            /**
             * A proprietary setting `stackByValue` which is not an
             * official config option. It will be used by our custom
             * plugin
             */
            "stackByValue": true,
            "axisAlpha": 0.3,
            "gridAlpha": 0
        }, , {
            "id": "v2",
            "axisAlpha": 0.3,
            "gridAlpha": 0,
            "position": "top",

            "title": "Target"
        }],
        "gridAboveGraphs": true,
        "startDuration": 0,
        "graphs": graphs,
        "categoryField": "name",
        "categoryAxis": {
            "gridPosition": "start",
            "axisAlpha": 0,
            "gridAlpha": 0,
            "position": "left"
        },
        "export": {
            "enabled": true
        }

    });

    console.log(graphs);
    console.log(response);

    Object.keys(response[0]).forEach(key => {
        console.log(key) // returns the keys in an object
        // console.log(a[key])  // returns the appropriate value 
    })
});

问题是按值排序插件假定所有图表都需要排序和修改才能使用 open/close 字段来实现此效果,这会导致它将项目符号移动到错误的位置。由于你有多个轴,你可以修改插件来检查图形是否属于第一个轴并设置一个标志以用于重新正确添加点:

  // Go through each category and order values
  for (var i = 0; i < chart.dataProvider.length; i++) {

    // ...
    for (var x = 0; x < chart.graphs.length; x++) {
      var graph = chart.graphs[x];
      items.push({
        "graph": graph,
        // check if this graph's data points need to be omitted from the sorting process.
        "ignoreSort": (graph.valueAxis && graph.valueAxis !== chart.valueAxes[0].id),
        "value": dp[graph.originalValueField]
      });
    }

    // ...

    // Calculate open and close fields
    var offset = 0;
    for (var x = 0; x < items.length; x++) {
      var item = items[x];

      if (!item.ignoreSort) {
        //process the pont as normal if it doesn't have the flag set with open/value fields
        dp[item.graph.openField] = offset;
        dp[item.graph.valueField] = offset + dp[item.graph.originalValueField];
        offset = dp[item.graph.valueField];
      } else {
        //otherwise treat the point as a normal graph and use the value field
        dp[item.graph.valueField] = dp[item.graph.originalValueField]
      }

    }
  }

您还需要同步轴的 min/max 值,以便您的目标相对于堆叠条正确放置。您可以通过 addInitHandler:

使用另一个自定义插件来实现此目的
//synchronizes axes' min/max values using a custom synchronizeValueAxes property
//(while synchronizeGrid exists, it doesn't work with this particular chart)
AmCharts.addInitHandler(function(chart) {
  if (chart.synchronizeValueAxes) {
    setTimeout(function() {
      var max = chart.valueAxes.reduce(function(max, axis) {
        if (!isNaN(axis.max)) {
          return Math.max(max, axis.max);
        } else {
          return max;
        }
      }, Number.MIN_VALUE);
      var min = chart.valueAxes.reduce(function(min, axis) {
        if (!isNaN(axis.min)) {
          return Math.min(min, axis.min);
        } else {
          return min;
        }

      }, Number.MAX_VALUE);

      chart.valueAxes.forEach(function(axis) {
        axis.maximum = max;
        axis.minimum = min;
        axis.strictMinMax = true;
      });
      chart.validateData();
    }, 500);
  }

}, ["serial"]);

Updated fiddle