防止amcharts系列重叠

时间:2019-01-18 17:43:21

标签: javascript typescript amcharts

我有一个图表设置,其中包含三个系列,每个系列都有各自的y轴。当前,它们显示在图的两侧,如下所示: Current Graph

这比我希望的图表更忙,因为有时该系列可以混合在一起。如何使y轴不重叠,如下所示: Desired Graph

编辑:这是一个working example

/**
 * ---------------------------------------
 * This demo was created using amCharts 4.
 *
 * For more information visit:
 * https://www.amcharts.com/
 *
 * Documentation is available at:
 * https://www.amcharts.com/docs/v4/
 * ---------------------------------------
 */

// Apply chart themes
am4core.useTheme(am4themes_animated);

// Create chart instance
var chart = am4core.create("chartdiv", am4charts.XYChart);
chart.paddingRight = 20;

var dateAxis = chart.xAxes.push(new am4charts.DateAxis());
dateAxis.baseInterval = {
  'timeUnit': 'second',
  'count': 1
};
dateAxis.dateFormats.setKey('minute', 'h:mm a');
dateAxis.tooltipDateFormat = '[bold]MM-dd-yy, h:mm:ss a[/]';
dateAxis.renderer.grid.template.location = 0;

chart.yAxes.push(new am4charts.ValueAxis());
chart.yAxes.push(new am4charts.ValueAxis());
chart.yAxes.push(new am4charts.ValueAxis());

chart.cursor = new am4charts.XYCursor();
chart.legend = new am4charts.Legend();


var data = [
  {name:'value1', value: 2, timestamp: '2018-01-01T15:00:00.00Z'},
  {name:'value2', value: 5, timestamp: '2018-01-01T15:00:00.00Z'},
  {name:'value3', value: 8, timestamp: '2018-01-01T15:00:00.00Z'},
  {name:'value1', value: 4, timestamp: '2018-01-01T15:00:15.00Z'},
  {name:'value2', value: 2, timestamp: '2018-01-01T15:00:15.00Z'},
  {name:'value3', value: 5, timestamp: '2018-01-01T15:00:15.00Z'},
  {name:'value1', value: 9, timestamp: '2018-01-01T15:00:30.00Z'},
  {name:'value2', value: 11, timestamp: '2018-01-01T15:00:30.00Z'},
  {name:'value3', value: 6, timestamp: '2018-01-01T15:00:30.00Z'},
  {name:'value1', value: 4, timestamp: '2018-01-01T15:00:45.00Z'},
  {name:'value2', value: 5, timestamp: '2018-01-01T15:00:45.00Z'},
  {name:'value3', value: 13, timestamp: '2018-01-01T15:00:45.00Z'},
  {name:'value1', value: 1, timestamp: '2018-01-01T15:01:00.00Z'},
  {name:'value2', value: 7, timestamp: '2018-01-01T15:01:00.00Z'},
  {name:'value3', value: 11, timestamp: '2018-01-01T15:01:00.00Z'},
];
var names = ['value1','value2', 'value3'];

names.forEach((name, index) => {
  var opposite = index % 2 == 0;
  var series = this.chart.series.push(new am4charts.LineSeries());
  var yAxis = this.chart.yAxes.getIndex(index);
  series.yAxis = yAxis;
  series.name = name;
  series.dataFields.dateX = 'date';
  series.dataFields.valueY = 'value';
  series.data = data.filter(d => d.name === name).map( d => {
    return {date: new Date(d.timestamp), value: d.value};
  });
  switch (name) {
    case 'value1':
      series.stroke = am4core.color('#9E842F');
      yAxis.minY = 0;
      yAxis.maxY = 31;
      break;
    case 'value2':
      series.stroke = am4core.color('#196D6F');
      yAxis.minY = -20;
      yAxis.maxY = 80;
      break;
    case 'value3':
      series.stroke = am4core.color('#553786');
      yAxis.minY = 0;
      yAxis.maxY = 100;
      break;
  }
  series.tooltip.getFillFromObject = false;
  series.tooltip.background.fill = series.stroke;
  series.tooltipText = '{name}: [bold]{valueY}[/]';

  yAxis.cursorTooltipEnabled = false;
  yAxis.renderer.line.strokeOpacity = 1;
  yAxis.renderer.line.strokeWidth = 2;
  yAxis.renderer.line.stroke = series.stroke;
  yAxis.renderer.labels.template.fill = series.stroke;
  yAxis.renderer.opposite = opposite;
  yAxis.renderer.grid.template.disabled = true;

  var bullet = series.bullets.push(new am4charts.CircleBullet());
  bullet.width = 5;
  bullet.height = 5;
  bullet.fill = series.stroke;
});

var scrollbarX = new am4charts.XYChartScrollbar();
chart.series.values.forEach(s => {
  scrollbarX.series.push(s);
});
chart.scrollbarX = scrollbarX;
chart.scrollbarX.parent = this.chart.bottomAxesContainer;

chart.validateData();

2 个答案:

答案 0 :(得分:2)

没有创建单独的图表就无法做到这一点,但是,如果您在同一div中创建单独的图表作为子容器对象,则可以同步光标并在每个实例上缩放。 github repo中有一个有关此权限的示例,其中它创建了三个单独的实例并使用事件将游标同步在一起。

以下是使用您的数据复制的演示:

am4core.useTheme(am4themes_animated);

var container = am4core.create("chartdiv", am4core.Container);
container.width = am4core.percent(100);
container.height = am4core.percent(100);
container.layout = "vertical";



var chartCount = 3;
var data = [
  {name:'value1', value: 2, timestamp: '2018-01-01T15:00:00.00Z'},
  {name:'value2', value: 5, timestamp: '2018-01-01T15:00:00.00Z'},
  {name:'value3', value: 8, timestamp: '2018-01-01T15:00:00.00Z'},
  {name:'value1', value: 4, timestamp: '2018-01-01T15:00:15.00Z'},
  {name:'value2', value: 2, timestamp: '2018-01-01T15:00:15.00Z'},
  {name:'value3', value: 5, timestamp: '2018-01-01T15:00:15.00Z'},
  {name:'value1', value: 9, timestamp: '2018-01-01T15:00:30.00Z'},
  {name:'value2', value: 11, timestamp: '2018-01-01T15:00:30.00Z'},
  {name:'value3', value: 6, timestamp: '2018-01-01T15:00:30.00Z'},
  {name:'value1', value: 4, timestamp: '2018-01-01T15:00:45.00Z'},
  {name:'value2', value: 5, timestamp: '2018-01-01T15:00:45.00Z'},
  {name:'value3', value: 13, timestamp: '2018-01-01T15:00:45.00Z'},
  {name:'value1', value: 1, timestamp: '2018-01-01T15:01:00.00Z'},
  {name:'value2', value: 7, timestamp: '2018-01-01T15:01:00.00Z'},
  {name:'value3', value: 11, timestamp: '2018-01-01T15:01:00.00Z'},
];
var charts = [];
var cursorShowDisposers = [];

// create chart instances
for (var i = 0; i < chartCount; i++) {
  makeChart(data.filter((x) => x.name == 'value' + (i + 1)), 'value' + (i + 1));
}

let legend = new am4charts.Legend();
legend.parent = container;
legend.interactionsEnabled = false;
legend.data = [{
  "name": "value1",
  "fill":"#9E842F"
}, {
  "name": "value2",
  "fill": "#196D6F"
}, {
  "name": "value3",
  "fill": "#553786"
}];

initCursorListeners();

// after the charts are made, add scrollbar to the first one
var firstChart = charts[0];
firstChart.scrollbarX = new am4core.Scrollbar();
firstChart.zoomOutButton.disabled = false;

// enable date axis labels for the last one
var lastChart = charts[charts.length - 1];
var lastDateAxis = lastChart.xAxes.getIndex(0);
lastDateAxis.renderer.labels.template.disabled = false;
lastDateAxis.cursorTooltipEnabled = true;



// create chart
function makeChart(data, name) {
  var chart = container.createChild(am4charts.XYChart);
  charts.push(chart);

  chart.data = data;
  chart.zoomOutButton.disabled = true;
  chart.padding(10, 15, 10, 15);

  var dateAxis = chart.xAxes.push(new am4charts.DateAxis());
  dateAxis.renderer.grid.template.location = 0;
  dateAxis.renderer.labels.template.disabled = true;
  dateAxis.tooltip.animationDuration = 0; 
  dateAxis.cursorTooltipEnabled = false;
  dateAxis.dateFormatter.inputDateFormat = 'i';
  dateAxis.baseInterval = {
    'timeUnit': 'second',
    'count': 1
  };
  
  var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
  valueAxis.tooltip.disabled = true;
  valueAxis.tooltip.disabled = true;
  valueAxis.renderer.minWidth = 60;
  
  var series = chart.series.push(new am4charts.LineSeries());
  series.dataFields.dateX = "timestamp";
  series.dataFields.valueY = "value";
  series.name = name;
  series.interpolationDuration = 0;

  switch (name) {
    case 'value1':
      series.stroke = am4core.color('#9E842F');
      valueAxis.minY = 0;
      valueAxis.maxY = 31;
      break;
    case 'value2':
      series.stroke = am4core.color('#196D6F');
      valueAxis.minY = -20;
      valueAxis.maxY = 80;
      break;
    case 'value3':
      series.stroke = am4core.color('#553786');
      valueAxis.minY = 0;
      valueAxis.maxY = 100;
      break;
  }
  series.tooltip.getFillFromObject = false;
  series.tooltip.background.fill = series.stroke;
  series.tooltipText = '{name}: [bold]{valueY}[/]';

  var bullet = series.bullets.push(new am4charts.CircleBullet());
  bullet.width = 5;
  bullet.height = 5;
  bullet.fill = series.stroke;
  
  var cursor = new am4charts.XYCursor();
  cursor.lineY.disabled = true;
  cursor.xAxis = dateAxis;
  chart.cursor = cursor;


  // whenever any of the charts is zoomed, we should zoom all other charts
  dateAxis.events.on("selectionextremeschanged", function (event) {
    syncDateAxes(event.target);
  })
}


function initCursorListeners() {
  cursorShowDisposers = [];
  for (var i = 0; i < charts.length; i++) {
    var chart = charts[i];
    var cursor = chart.cursor;
    cursor.interactionsEnabled = true;

    cursorShowDisposers.push(cursor.events.on("shown", function (event) {
      handleShowCursor(event.target);
    }));
  }
}

var shownCursorChangeDisposer;
var shownCursorZoomStartedDisposer;
var shownCursorZoomEndedDisposer;

function handleShowCursor(shownCursor) {
  // disable mouse for all other cursors
  for (var i = 0; i < charts.length; i++) {
    var chart = charts[i];
    var cursor = chart.cursor;
    if (cursor != shownCursor) {
      cursor.interactionsEnabled = false;
    }
    // remove show listener
    cursorShowDisposers[i].dispose();
  }

  // add change disposer to the hovered chart cursor
  shownCursorChangeDisposer = shownCursor.lineX.events.on("positionchanged", function (event) {
    syncCursors(shownCursor);
  });


  shownCursorZoomStartedDisposer = shownCursor.events.on("zoomstarted", function (event) {

    for (var i = 0; i < charts.length; i++) {
      var chart = charts[i];
      var cursor = chart.cursor;
      if (cursor != event.target) {
        var point = { x: event.target.point.x, y: 0 };
        cursor.triggerDown(point);
      }
    }
  });

  shownCursorZoomEndedDisposer = shownCursor.events.on("zoomended", function (event) {
    for (var i = 0; i < charts.length; i++) {
      var chart = charts[i];
      var cursor = chart.cursor;
      if (cursor != event.target) {
        var point = { x: event.target.point.x, y: 0 };
        cursor.triggerUp(point);
      }
    }
  });


  shownCursor.events.once("hidden", function (event) {
    shownCursorChangeDisposer.dispose();
    shownCursorZoomStartedDisposer.dispose();
    shownCursorZoomEndedDisposer.dispose();

    for (var i = 0; i < charts.length; i++) {
      var chart = charts[i];
      var cursor = chart.cursor;
      cursor.hide(0);

      cursorShowDisposers[i].dispose();
    }

    initCursorListeners();
  });
}

function syncCursors(syncWithCursor) {
  for (var i = 0; i < charts.length; i++) {
    var chart = charts[i];
    var cursor = chart.cursor;

    var point = { x: syncWithCursor.point.x, y: 0 };

    if (cursor != syncWithCursor) {
      cursor.triggerMove(point);
    }
  }
}


function syncDateAxes(syncWithAxis) {
  for (var i = 0; i < charts.length; i++) {
    var chart = charts[i];
    var dateAxis = chart.xAxes.getIndex(0);
    if (dateAxis != syncWithAxis) {
      dateAxis.events.disableType("selectionextremeschanged");
      dateAxis.start = syncWithAxis.start;
      dateAxis.end = syncWithAxis.end;
      dateAxis.events.enableType("selectionextremeschanged");
    }
  }
}
;
#chartdiv {
  width: 100%;
  height: 700px;
}
<script src="//www.amcharts.com/lib/4/core.js"></script>
<script src="//www.amcharts.com/lib/4/charts.js"></script>
<script src="//www.amcharts.com/lib/4/themes/animated.js"></script>
<div id="chartdiv"></div>

答案 1 :(得分:0)

通过一些小的调整,就可以使所有三个轴堆叠在单个图表中,而光标,滚动条和缩放会影响所有三个轴:

  • 主要技巧是设置chart.leftAxesContainer.layout = 'vertical',以使所有三个轴垂直堆叠在容器中

  • 要使其正常工作,请不要在任何轴上设置opposite(我们希望它们都位于左侧)

  • 删除yAxis上设置minYmaxY的行,以免丑陋重叠

  • 在每个轴上设置合适的高度和边距,如下所示:yAxis.height = 150; yAxis.marginBottom = 20;

  • 更改CSS中图表的整体高度,使其更高,并防止所有内容挤在一起(例如#chartdiv { height: 700px; }

这里是modified version of your example,并且(如果Codepen停止存在,则是以下代码):

/**
 * ---------------------------------------
 * This demo was created using amCharts 4.
 *
 * For more information visit:
 * https://www.amcharts.com/
 *
 * Documentation is available at:
 * https://www.amcharts.com/docs/v4/
 * ---------------------------------------
 */

// Apply chart themes
am4core.useTheme(am4themes_animated);

// Create chart instance
var chart = am4core.create("chartdiv", am4charts.XYChart);
chart.paddingRight = 20;
chart.leftAxesContainer.layout = 'vertical'

var dateAxis = chart.xAxes.push(new am4charts.DateAxis());
dateAxis.baseInterval = {
  'timeUnit': 'second',
  'count': 1
};
dateAxis.dateFormats.setKey('minute', 'h:mm a');
dateAxis.tooltipDateFormat = '[bold]MM-dd-yy, h:mm:ss a[/]';
dateAxis.renderer.grid.template.location = 0;

chart.yAxes.push(new am4charts.ValueAxis());
chart.yAxes.push(new am4charts.ValueAxis());
chart.yAxes.push(new am4charts.ValueAxis());

chart.cursor = new am4charts.XYCursor();
chart.legend = new am4charts.Legend();


var data = [
  {name:'value1', value: 2, timestamp: '2018-01-01T15:00:00.00Z'},
  {name:'value2', value: 5, timestamp: '2018-01-01T15:00:00.00Z'},
  {name:'value3', value: 8, timestamp: '2018-01-01T15:00:00.00Z'},
  {name:'value1', value: 4, timestamp: '2018-01-01T15:00:15.00Z'},
  {name:'value2', value: 2, timestamp: '2018-01-01T15:00:15.00Z'},
  {name:'value3', value: 5, timestamp: '2018-01-01T15:00:15.00Z'},
  {name:'value1', value: 9, timestamp: '2018-01-01T15:00:30.00Z'},
  {name:'value2', value: 11, timestamp: '2018-01-01T15:00:30.00Z'},
  {name:'value3', value: 6, timestamp: '2018-01-01T15:00:30.00Z'},
  {name:'value1', value: 4, timestamp: '2018-01-01T15:00:45.00Z'},
  {name:'value2', value: 5, timestamp: '2018-01-01T15:00:45.00Z'},
  {name:'value3', value: 13, timestamp: '2018-01-01T15:00:45.00Z'},
  {name:'value1', value: 1, timestamp: '2018-01-01T15:01:00.00Z'},
  {name:'value2', value: 7, timestamp: '2018-01-01T15:01:00.00Z'},
  {name:'value3', value: 11, timestamp: '2018-01-01T15:01:00.00Z'},
];
var names = ['value1','value2', 'value3'];

names.forEach((name, index) => {
  var series = this.chart.series.push(new am4charts.LineSeries());
  var yAxis = this.chart.yAxes.getIndex(index);
  yAxis.height = 150;
  yAxis.marginBottom = 20;
  series.yAxis = yAxis;
  series.name = name;
  series.dataFields.dateX = 'date';
  series.dataFields.valueY = 'value';
  series.data = data.filter(d => d.name === name).map( d => {
    return {date: new Date(d.timestamp), value: d.value};
  });
  switch (name) {
    case 'value1':
      series.stroke = am4core.color('#9E842F');
      break;
    case 'value2':
      series.stroke = am4core.color('#196D6F');
      break;
    case 'value3':
      series.stroke = am4core.color('#553786');
      break;
  }
  series.tooltip.getFillFromObject = false;
  series.tooltip.background.fill = series.stroke;
  series.tooltipText = '{name}: [bold]{valueY}[/]';

  yAxis.cursorTooltipEnabled = false;
  yAxis.renderer.line.strokeOpacity = 1;
  yAxis.renderer.line.strokeWidth = 2;
  yAxis.renderer.line.stroke = series.stroke;
  yAxis.renderer.labels.template.fill = series.stroke;
  yAxis.renderer.grid.template.disabled = true;

  var bullet = series.bullets.push(new am4charts.CircleBullet());
  bullet.width = 5;
  bullet.height = 5;
  bullet.fill = series.stroke;
});

var scrollbarX = new am4charts.XYChartScrollbar();
chart.series.values.forEach(s => {
  scrollbarX.series.push(s);
});
chart.scrollbarX = scrollbarX;
chart.scrollbarX.parent = this.chart.bottomAxesContainer;

chart.validateData();