How to position NVD3 line graph above all other area / bar graphs?

|▌冷眼眸甩不掉的悲伤 提交于 2019-12-10 18:22:20

问题


The Plunker example

In the example above you can see that the line is rendered under the orange area graph:

Was reading this trend here, then I tried this d3.select('svg#chart .lines1Wrap').moveToFront();, but got the error moveToFront is not a function.

Chart code

var data = [{
  "key": "Price",
  "type": "line",
  "yAxis": 2,
  "values": [
    [1443621600000, 71.89],
    [1443619800000, 75.51],
    [1443618000000, 12.49],
    [1443616200000, 20.72],
    [1443612600000, 70.39],
    [1443610800000, 59.77],
  ]
}, {
  "key": "Quantity1",
  "type": "area",
  "yAxis": 1,
  "values": [
    [1136005200000, 1],
    [1138683600000, 5],
    [1141102800000, 10],
    [1143781200000, 0],
    [1146369600000, 1],
    [1149048000000, 0],
  ]
}];

data = data.map(function(series) {
  series.values = series.values.map(function(d) {
    return {
      x: d[0],
      y: d[1]
    }
  });
  return series;
});

nv.addGraph(function() {
  var chart = nv.models.multiChart()
    .margin({
      top: 20,
      right: 40,
      bottom: 50,
      left: 40
    })
    .yDomain1([0, 10])
    .yDomain2([0, 100]) // hard-coded :<
    .interpolate("linear") // don't smooth out the lines
    .color(d3.scale.category10().range());

  chart.xAxis.tickFormat(function(d) {
    return d3.time.format('%I:%M')(new Date(d));
  });
  chart.yAxis1.tickFormat(d3.format(',f'));
  chart.yAxis2.tickFormat(function(d) {
    return '$' + d3.format(',f')(d)
  });

  d3.select('svg#chart')
    .datum(data)
    .transition().duration(500).call(chart);

  d3.selection.prototype.moveToFront = function() {
      return this.each(function() {
          this.parentNode.appendChild(this);
      });
  };

  d3.select('svg#chart .lines1Wrap').moveToFront();

  chart.update();
  nv.utils.windowResize(chart.update);
  return chart;
});

UPDATE Found this answer here, tried to use the solution:

d3.selection.prototype.moveToFront = function() {
    return this.each(function() {
        this.parentNode.appendChild(this);
    });
};

d3.selection.prototype.moveToBack = function() { 
    return this.each(function() { 
        var firstChild = this.parentNode.firstChild; 
        if (firstChild) { 
            this.parentNode.insertBefore(this, firstChild); 
        } 
    }); 
};

d3.select('svg#chart .lines1Wrap').moveToFront();
d3.select('svg#chart .nv-areaWrap').moveToBack();

No more error, however the blue line graph still is not moved in front of all the others.


回答1:


Add this to move your line(line chart) DOM element after the line(Area Chart) DOM element.

d3.select('.lines2Wrap').node().parentNode.insertBefore(d3.select('.stack1Wrap').node(), d3.select('.lines2Wrap').node());

Full working code here

Hoe this helps! :)




回答2:


As I struggled with a similar problem for two days I will post my solution here in case it helps someone.

In my chart settings I have dispatch function which will append a rectangle to the chart and then lower it to the bottom:

dispatch: {
  renderEnd: () => {
     drawThresholds();
     lowerThresholdAreas();
  }
}

In the drawThresholds I draw the actual rectangle:

const drawThresholds = () => {
  try {
    let svgContainer = d3.select(`svg g`);

     svgContainer.append('rect')
        .attr('x', 0)
        .attr('y', 2)
        .attr('width', 200)
        .attr('height', 200)
        .attr('class', 'threshold_area')
        .attr('fill', 'yellow');
    }
    catch (err) {
    // Selector throws an error instead of returning empty list
    // when element is not found.
    // Ignore exceptions, they occur because page is not loaded yet.
    }
  };

And finally after rectangles are appended, I need to place them to the bottom by calling lowerFunction. use d3 selectAll to get all .threshold_area classes and then insert them before the line.

const lowerThresholdAreas = () => {
  d3.selectAll('.threshold_area').each(function () {
    this.parentNode.insertBefore(this, this.parentNode.firstChild);
  });
};

And what is happening, is that on SVG canvas there is no z-index. It means the order of appends defines the layers, meaning if you append the rectangle as last item, it will be on top. So the order of elements needs to be changed.

Also a step where I did mistake was that I first used ES6 function declaration and it doesn't work with "this" as I supposed it would.

Read more about selection: https://github.com/d3/d3-selection/blob/master/README.md#select

Read more about lowering or raising items: https://github.com/d3/d3-selection/blob/master/README.md#selection_lower

Here is a working plunker for seeing it in action: https://plnkr.co/edit/QI2HcxcJYRAv0FzXWihd?p=preview




回答3:


I think it's just that on your particular graph, the line series is under lines2wrap, not lines1wrap. IIRC, this has to do with which series is 'first'. In your plunker, I changed the 1 to a 2 and it worked.

It's a hacky workaround (that's mine from Github, thanks for notifying me), and probably requires some tinkering to be more generally useful.



来源:https://stackoverflow.com/questions/33000725/how-to-position-nvd3-line-graph-above-all-other-area-bar-graphs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!