Search code examples
javascriptamcharts4

AmCharts4 line chart with vertically draggable bullets which don't follow the zoom


Here is a line chart with vertically draggable bullets. Problem: when we zoom the x-axis (with the scrollbar), the bullets don't follow.

am4core.useTheme(am4themes_animated);

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

// Add data
chart.data = [
  {
    date: new Date(2018, 0, 1),
    value: 450
  },
  {
    date: new Date(2018, 0, 2),
    value: 269
  },
  {
    date: new Date(2018, 0, 3),
    value: 700
  },
  {
    date: new Date(2018, 0, 4),
    value: 490
  },
  {
    date: new Date(2018, 0, 5),
    value: 500
  },
  {
    date: new Date(2018, 0, 6),
    value: 550
  },
  {
    date: new Date(2018, 0, 7),
    value: 420
  }
];

// Create axes
var dateAxis = chart.xAxes.push(new am4charts.DateAxis());
dateAxis.renderer.grid.template.location = 0;
dateAxis.renderer.minGridDistance = 40;

var valueAxis = chart.yAxes.push(new am4charts.ValueAxis());

// Create series
function createSeries(field, name) {
  var series = chart.series.push(new am4charts.LineSeries());
  series.dataFields.valueY = field;
  series.dataFields.dateX = "date";
  series.name = name;
  series.tooltipText = "{dateX}: [b]{valueY}[/]";
  series.strokeWidth = 2;

  var bullet = series.bullets.push(new am4charts.CircleBullet());
  bullet.circle.stroke = am4core.color("#fff");
  bullet.circle.strokeWidth = 2;

  bullet.draggable = true;
  // resize cursor when over
  bullet.cursorOverStyle = am4core.MouseCursorStyle.verticalResize;
  // while dragging
  bullet.events.on("drag", (event) => {
    handleDrag(event);
  });
  // on dragging stop
  bullet.events.on("dragstop", (event) => {
    handleDragStop(event);
  });
  // when line position changes, adjust minX/maxX of bullets so that we could only dragg vertically
  bullet.events.on("positionchanged", (event) => {
    let dataItem = event.target.dataItem;
    if (dataItem.bullets) {
      let itemBullet = dataItem.bullets.getKey(bullet.uid);
      let point = dataItem.point;
      itemBullet.minX = point.x;
      itemBullet.maxX = itemBullet.minX;
      itemBullet.minY = 0;
      itemBullet.maxY = chart.seriesContainer.pixelHeight;
    }
  });
}

createSeries("value", "Series #1");

chart.cursor = new am4charts.XYCursor();

chart.scrollbarX = new am4core.Scrollbar();

/* ~~~~\  function handling the drag event  /~~~~ */
function handleDrag(event) {
  let dataItem = event.target.dataItem;
  // convert coordinate to value
  let value = valueAxis.yToValue(event.target.pixelY);
  // set new value
  dataItem.valueY = value;
  // make line hover
  dataItem.segment.isHover = true;
  // hide tooltip not to interrupt
  dataItem.segment.hideTooltip(0);
  // make bullet hovered (as it might hide if mouse moves away)
  event.target.isHover = true;
}

/* ~~~~\  function handling the dragstop event  /~~~~ */
function handleDragStop(event) {
  handleDrag(event);
  let dataItem = event.target.dataItem;
  dataItem.component.isHover = false; 
  event.target.isHover = false;
}
#chartdiv {
  width: 100%;
  height: 500px;
}
<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>


Solution

  • I think I have a solution:

    dateAxis.events.on("startchanged", event => {
        event.target.series.each(function(s) {
            s.bulletsContainer.children.each(function(b) {
                b.dispatch("positionchanged");
            });
        });
    });
    
    dateAxis.events.on("endchanged", event => {
        event.target.series.each(function(s) {
            s.bulletsContainer.children.each(function(b) {
                b.dispatch("positionchanged");
            });
        });
    });
    

    Or, shorter:

    dateAxis.events.on("selectionextremeschanged", event => {
        event.target.series.each(function(s) {
            s.bulletsContainer.children.each(function(b) {
                b.dispatch("positionchanged");
            });
        });
    });