Автоматическое усечение длинных меток осей категорий, но отображение полной метки в легенде?

1

Я использую amchart для графика. Ниже приведен код,

var chart = AmCharts.makeChart("chartdiv", {
    "theme": "light",
    "type": "serial",
    "startDuration": 2,
    "dataProvider": [{
        "country": "This is Sample Data with long label",
        "visits": 4025,
        "color": "#FF0F00"
    }, {
        "country": "This is Sample Data with long label1",
        "visits": 1882,
        "color": "#FF6600"
    }, {
        "country": "This is Sample Data with long label2",
        "visits": 1809,
        "color": "#FF9E01"
    }, {
        "country": "This is Sample Data with long label3",
        "visits": 1322,
        "color": "#FCD202"
    }, {
        "country": "This is Sample Data with long label4",
        "visits": 1122,
        "color": "#F8FF01"
    }, {
        "country": "This is Sample Data with long label5",
        "visits": 1114,
        "color": "#B0DE09"
    }, {
        "country": "This is Sample Data with long label6",
        "visits": 984,
        "color": "#04D215"
    }, {
        "country": "This is Sample Data with long label7",
        "visits": 711,
        "color": "#0D8ECF"
    }, {
        "country": "This is Sample Data with long label8",
        "visits": 665,
        "color": "#0D52D1"
    }, {
        "country": "This is Sample Data with long label9",
        "visits": 580,
        "color": "#2A0CD0"
    }, {
        "country": "This is Sample Data with long label10",
        "visits": 443,
        "color": "#8A0CCF"
    }, {
        "country": "This is Sample Data with long label11",
        "visits": 441,
        "color": "#CD0D74"
    }, {
        "country": "This is Sample Data with long label12",
        "visits": 395,
        "color": "#754DEB"
    }, {
        "country": "This is Sample Data with long label13",
        "visits": 386,
        "color": "#DDDDDD"
    }, {
        "country": "This is Sample Data with long label14",
        "visits": 338,
        "color": "#333333"
    }],
    "valueAxes": [{
        "position": "left",
        "axisAlpha":0,
        "gridAlpha":0
    }],
    "graphs": [{
        "balloonText": "[[category]]: <b>[[value]]</b>",
        "colorField": "color",
        "fillAlphas": 0.85,
        "lineAlpha": 0.1,
        "type": "column",
        "topRadius":1,
        "valueField": "visits"
    }],
    "depth3D": 40,
    "angle": 30,
    "chartCursor": {
        "categoryBalloonEnabled": false,
        "cursorAlpha": 0,
        "zoomable": false
    },
    "categoryField": "country",
    "categoryAxis": {
        "gridPosition": "start",
        "axisAlpha":0,
        "gridAlpha":0

    },
  "labelFunction": function(label, item, axis) {
      var chart = axis.chart;
      if ( (chart.realWidth <= 300 ) && ( label.length > 5 ) )
        return label.substr(0, 5) + '...';
      if ( (chart.realWidth <= 500 ) && ( label.length > 10 ) )
        return label.substr(0, 10) + '...';
      return label;
    },
  "legend": {
    "useGraphSettings": true
  },
    "export": {
        "enabled": true
     }

}, 0);

Однако ярлык Xaxis очень длинный, я хотел бы автоматически обрезать ярлыки оси длинной категории, как этот пример, а также включить легенду. Но включение легенды не работает, также автоматическое усечение не работает. Может ли кто-нибудь помочь мне здесь? Заранее спасибо.

Вот ссылка на codepen [1].

[1] https://codepen.io/gknathkumar/pen/OxKGev

  • 0
    Что вы подразумеваете под легендой не работает? Легенда отображается в виде одного графического объекта, поэтому имеется только один маркер. Это по замыслу. Вы ищете что-то вроде этого, которое создает фальшивые маркеры для каждого столбца?
  • 0
    @xorspark, это именно то, что я ищу.
Показать ещё 1 комментарий
Теги:
amcharts

3 ответа

2
Лучший ответ

Как указывали другие, labelFunction является частью категорииAxis, поэтому он должен туда попасть. Я частично отношусь к методу в реализации кузина, но выбираю то, что вам нужно.

Что касается легенды, она генерируется объектами графика по дизайну. Поскольку существует один объект графика, есть только один маркер. Добавление маркера для каждого столбца требует добавления специального кода, который изменяет массив data легенды для создания настраиваемых маркеров. У AmCharts есть статья базы знаний для создания маркеров для каждого столбца. Соответствующий код:

/*
  Plugin to generate legend markers based on category
  and fillColor/lineColor/color field from the chart data by using 
  the legend custom data array. Also allows for toggling markers
  by completely removing/adding columns from the chart

  The plugin assumes there is  only one graph object. 
*/
AmCharts.addInitHandler(function(chart) { 

  //method to handle removing/adding columns when the marker is toggled
  function handleCustomMarkerToggle(legendEvent) {
      var dataProvider = legendEvent.chart.dataProvider;
      var itemIndex; //store the location of the removed item

      //Set a custom flag so that the dataUpdated event doesn't fire infinitely, in case you have
      //a dataUpdated event of your own
      legendEvent.chart.toggleLegend = true; 
      // The following toggles the markers on and off.
      // The only way to "hide" a column and reserved space on the axis is to remove it
      // completely from the dataProvider. You'll want to use the hidden flag as a means
      // to store/retrieve the object as needed and then sort it back to its original location
      // on the chart using the dataIdx property in the init handler
      if (undefined !== legendEvent.dataItem.hidden && legendEvent.dataItem.hidden) {
        legendEvent.dataItem.hidden = false;
        dataProvider.push(legendEvent.dataItem.storedObj);
        legendEvent.dataItem.storedObj = undefined;
        //re-sort the array by dataIdx so it comes back in the right order.
        dataProvider.sort(function(lhs, rhs) {
          return lhs.dataIdx - rhs.dataIdx;
        });
      } else {
        // toggle the marker off
        legendEvent.dataItem.hidden = true;
        //get the index of the data item from the data provider, using the 
        //dataIdx property.
        for (var i = 0; i < dataProvider.length; ++i) { 
          if (dataProvider[i].dataIdx === legendEvent.dataItem.dataIdx) {
            itemIndex = i;
            break;
          }
        }
        //store the object into the dataItem
        legendEvent.dataItem.storedObj = dataProvider[itemIndex];
        //remove it
        dataProvider.splice(itemIndex, 1);
      }
      legendEvent.chart.validateData(); //redraw the chart
  }

  //check if legend is enabled and custom generateFromData property
  //is set before running
  if (!chart.legend || !chart.legend.enabled || !chart.legend.generateFromData) {
    return;
  }

  var categoryField = chart.categoryField;
  var colorField = chart.graphs[0].lineColorField || chart.graphs[0].fillColorsField || chart.graphs[0].colorField;
  var legendData =  chart.dataProvider.map(function(data, idx) {
    var markerData = {
      "title": data[categoryField] + ": " + data[chart.graphs[0].valueField], 
      "color": data[colorField],
      "dataIdx": idx //store a copy of the index of where this appears in the dataProvider array for ease of removal/re-insertion
    };
    if (!markerData.color) {
      markerData.color = chart.graphs[0].lineColor;
    }
    data.dataIdx = idx; //also store it in the dataProvider object itself
    return markerData;
  });

  chart.legend.data = legendData;

  //make the markers toggleable
  chart.legend.switchable = true;
  chart.legend.addListener("clickMarker", handleCustomMarkerToggle);

}, ["serial"]);

Этот плагин требует, чтобы вы установили собственный флаг generateFromData в true в вашей легенде и ничего больше ( useGraphSettings несовместим):

  "legend": { 
    "generateFromData": true //custom property for the plugin
  },

Здесь приведена демоверсия, использующая метод укладки кузина и вышеупомянутый плагин:

/*
  Plugin to generate legend markers based on category
  and fillColor/lineColor/color field from the chart data by using 
  the legend custom data array. Also allows for toggling markers
  by completely removing/adding columns from the chart
  
  The plugin assumes there is  only one graph object. 
*/
AmCharts.addInitHandler(function(chart) { 
  
  //method to handle removing/adding columns when the marker is toggled
  function handleCustomMarkerToggle(legendEvent) {
      var dataProvider = legendEvent.chart.dataProvider;
      var itemIndex; //store the location of the removed item

      //Set a custom flag so that the dataUpdated event doesn't fire infinitely, in case you have
      //a dataUpdated event of your own
      legendEvent.chart.toggleLegend = true; 
      // The following toggles the markers on and off.
      // The only way to "hide" a column and reserved space on the axis is to remove it
      // completely from the dataProvider. You'll want to use the hidden flag as a means
      // to store/retrieve the object as needed and then sort it back to its original location
      // on the chart using the dataIdx property in the init handler
      if (undefined !== legendEvent.dataItem.hidden && legendEvent.dataItem.hidden) {
        legendEvent.dataItem.hidden = false;
        dataProvider.push(legendEvent.dataItem.storedObj);
        legendEvent.dataItem.storedObj = undefined;
        //re-sort the array by dataIdx so it comes back in the right order.
        dataProvider.sort(function(lhs, rhs) {
          return lhs.dataIdx - rhs.dataIdx;
        });
      } else {
        // toggle the marker off
        legendEvent.dataItem.hidden = true;
        //get the index of the data item from the data provider, using the 
        //dataIdx property.
        for (var i = 0; i < dataProvider.length; ++i) { 
          if (dataProvider[i].dataIdx === legendEvent.dataItem.dataIdx) {
            itemIndex = i;
            break;
          }
        }
        //store the object into the dataItem
        legendEvent.dataItem.storedObj = dataProvider[itemIndex];
        //remove it
        dataProvider.splice(itemIndex, 1);
      }
      legendEvent.chart.validateData(); //redraw the chart
  }

  //check if legend is enabled and custom generateFromData property
  //is set before running
  if (!chart.legend || !chart.legend.enabled || !chart.legend.generateFromData) {
    return;
  }
  
  var categoryField = chart.categoryField;
  var colorField = chart.graphs[0].lineColorField || chart.graphs[0].fillColorsField || chart.graphs[0].colorField;
  var legendData =  chart.dataProvider.map(function(data, idx) {
    var markerData = {
      "title": data[categoryField] + ": " + data[chart.graphs[0].valueField], 
      "color": data[colorField],
      "dataIdx": idx //store a copy of the index of where this appears in the dataProvider array for ease of removal/re-insertion
    };
    if (!markerData.color) {
      markerData.color = chart.graphs[0].lineColor;
    }
    data.dataIdx = idx; //also store it in the dataProvider object itself
    return markerData;
  });
  
  chart.legend.data = legendData;
  
  //make the markers toggleable
  chart.legend.switchable = true;
  chart.legend.addListener("clickMarker", handleCustomMarkerToggle);
  
}, ["serial"]);


// keep the data object separate from the call
var dataProvider = [
  {
    country: "This is Sample Data with long label",
    visits: 4025,
    color: "#FF0F00"
  },
  {
    country: "This is Sample Data with long label1",
    visits: 1882,
    color: "#FF6600"
  },
  {
    country: "This is Sample Data with long label2",
    visits: 1809,
    color: "#FF9E01"
  },
  {
    country: "This is Sample Data with long label3",
    visits: 1322,
    color: "#FCD202"
  }
];

var chart = AmCharts.makeChart(
  "chartdiv",
  {
    theme: "light",
    type: "serial",
    startDuration: 2,
    dataProvider: dataProvider,
    valueAxes: [
      {
        position: "left",
        axisAlpha: 0,
        gridAlpha: 0
      }
    ],
    graphs: [
      {
        balloonText: "[[category]]: <b>[[value]]</b>",
        colorField: "color",
        fillAlphas: 0.85,
        lineAlpha: 0.1,
        type: "column",
        topRadius: 1,
        valueField: "visits"
      }
    ],
    depth3D: 40,
    angle: 30,
    chartCursor: {
      categoryBalloonEnabled: false,
      cursorAlpha: 0,
      zoomable: false
    },
    categoryField: "country",
    categoryAxis: {
      gridPosition: "start",
      axisAlpha: 0,
      gridAlpha: 0,
      labelFunction: trimLabel,
    },
    legend: { 
      generateFromData: true //custom property for the plugin
    },
    export: {
      enabled: true
    }
  },
  0
);

// function to trim the labels
function trimLabel(label, item, axis) {
  var chartWidth = axis.chart.realWidth;
  var maxLabelLength = 15; // not counting the dots...

  // trim when the width of the chart is smalled than 300px
  if (chartWidth <= 300 && label.length > 5)
    return label.substr(0, 5) + "...";

  // trim when the width of the chart is smalled than 500px
  if (chartWidth <= 500 && label.length > 10)
    return label.substr(0, 10) + "...";

  // trim when label is longer than maxLabelLength regardless of chart width
  return label.length >= 15 ? label.substr(0, 14) + "...": label;
}
#chartdiv {
  width: 990px;
  height: 365px;
  border-radius: 3px;
  margin: 0px;
  border: 1px dotted #728FCE;
}
<script src="https://www.amcharts.com/lib/3/amcharts.js"></script>
<script src="https://www.amcharts.com/lib/3/serial.js"></script>
<script src="https://www.amcharts.com/lib/3/plugins/export/export.min.js"></script>
<link rel="stylesheet" href="https://www.amcharts.com/lib/3/plugins/export/export.css" type="text/css" media="all" />
<script src="https://www.amcharts.com/lib/3/themes/light.js"></script>
<input type="button" value="Set width to 300px" onclick="document.getElementById('chartdiv').style.width='300px';" />
<input type="button" value="Set width to 500px" onclick="document.getElementById('chartdiv').style.width='500px';" />
<input type="button" value="Set width to 700px" onclick="document.getElementById('chartdiv').style.width='700px';" />
<div id="chartdiv"></div>

Обратите внимание: если вы хотите, чтобы метки на маркерах были обрезаны, вам придется называть обрезку при создании названий маркеров в initHandler.

  • 1
    Спасибо за подробный ответ.
2

В вашем коде есть небольшая ошибка:

  • labelFunction не находится в categoryAxis
  • размер вашей диаграммы никогда не опускается ниже 500 пикселей, поэтому метки никогда не были обрезаны, как в примере
  • некоторые из кода могли быть помещены в переменные, чтобы сделать проще отлаживать

Я отделил часть кода и добавил максимальную длину (15 символов) для меток независимо от ширины диаграммы

Посмотреть полный пример на Codepen

// keep the data object separate from the call
var dataProvider = [
  {
    country: "This is Sample Data with long label",
    visits: 4025,
    color: "#FF0F00"
  },
  {
    country: "This is Sample Data with long label1",
    visits: 1882,
    color: "#FF6600"
  },
  {
    country: "This is Sample Data with long label2",
    visits: 1809,
    color: "#FF9E01"
  },
  {
    country: "This is Sample Data with long label3",
    visits: 1322,
    color: "#FCD202"
  }
];

var chart = AmCharts.makeChart(
  "chartdiv",
  {
    theme: "light",
    type: "serial",
    startDuration: 2,
    dataProvider: dataProvider,
    valueAxes: [
      {
        position: "left",
        axisAlpha: 0,
        gridAlpha: 0
      }
    ],
    graphs: [
      {
        balloonText: "[[category]]: <b>[[value]]</b>",
        colorField: "color",
        fillAlphas: 0.85,
        lineAlpha: 0.1,
        type: "column",
        topRadius: 1,
        valueField: "visits"
      }
    ],
    depth3D: 40,
    angle: 30,
    chartCursor: {
      categoryBalloonEnabled: false,
      cursorAlpha: 0,
      zoomable: false
    },
    categoryField: "country",
    categoryAxis: {
      gridPosition: "start",
      axisAlpha: 0,
      gridAlpha: 0,
      labelFunction: trimLabel,
    },
    legend: {
      useGraphSettings: true
    },
    export: {
      enabled: true
    }
  },
  0
);

// function to trim the labels
function trimLabel(label, item, axis) {
  var chartWidth = axis.chart.realWidth;
  var maxLabelLength = 15; // not counting the dots...

  // trim when the width of the chart is smalled than 300px
  if (chartWidth <= 300 && label.length > 5)
    return label.substr(0, 5) + "...";

  // trim when the width of the chart is smalled than 500px
  if (chartWidth <= 500 && label.length > 10)
    return label.substr(0, 10) + "...";

  // trim when label is longer than maxLabelLength regardless of chart width
  return label.length >= 15 ? label.substr(0, 14) + "...": label;
}
  • 0
    Спасибо @kuzyn, и здесь я вижу, что легенда не появляется должным образом.
2

make labelFunction как labelFunction ниже:

"labelFunction": function(label, item, axis) {

        var chart = axis.chart;
         console.log("CHART:", chart.realWidth, label.length, label );
          if ( ( label.length > 5 ) ){
            console.log("CHARTLABEL:", label.substr(0, 5) + '...');
            return label.substr(0, 7) + '...';            
          }
          if ( ( label.length > 10 ) ){
            return label.substr(0, 10) + '...';
          }
          return label;
        },

И ваш код не работал, потому что вы должны поместить функцию метки внутри categoryAxis

Окончательное рабочее решение: https://codepen.io/anon/pen/aLerBZ?editors=0010

  • 0
    Спасибо @ shyammakwana.me, но я вижу, что легенда все еще не отображается должным образом. Есть идеи, что там происходит?
  • 0
    Это другая проблема, я не смотрел глубоко, так что понятия не имею. Но, возможно, вы что-то упустили.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню