Search code examples
d3.jssvglinegraph

D3 how to find the height of the last point in a line graph


I am very new to d3, somehow have been able to draw a line graph with animation as following:

 var lineData = _.first(data.data, 30); // some array of numbers

            var  xRange = d3.scale.linear().range([MARGINS.left, WIDTH - MARGINS.right]).domain([0, 30]),
              yRange = d3.scale.linear().range([HEIGHT - MARGINS.top, MARGINS.bottom]).domain([_.min(lineData), _.max(lineData)]);

            var lineFunc = d3.svg.line()
              .x(function(d) {
                return xRange(lineData.indexOf(d));
              })
              .y(function(d) {
                return yRange(d);
              })
              .interpolate('basis');

            var path = vis.append('path')
              .attr('d', lineFunc(lineData))
              .attr('stroke', color)
              .attr('stroke-width', 3)
              .attr('fill', 'none');

            var totalLength = path.node().getTotalLength();

            path
              .attr("stroke-dasharray", totalLength + " " + totalLength)
              .attr("stroke-dashoffset", totalLength)
              .transition()
              .duration(5000)
              .delay(index * 1000)
              .ease("linear")
              .attr("stroke-dashoffset", 0);

Now I want to add at the end of each line some text(denoting name of each line). I can do that using the following code

vis.append("text")
          .attr("transform", "translate(" + (WIDTH - 15) + "," + 200 + ")")
          .attr("text-anchor", "start")
          .style("fill", "steelblue")
          .text("Close");

Now my problem is that the text node to be appended has to be at height where the line graph ends( and replace the currently hardcoded y=200). I am not able to achieve that , please help.


Solution

  • As per this example : http://bl.ocks.org/d3noob/8603837

    svg.append("text")
            .attr("transform", "translate(" + (width+3) + "," + y(data[0].close) + ")")
            .attr("dy", ".35em")
            .attr("text-anchor", "start")
            .style("fill", "steelblue")
            .text("Close");
    

    So in your case, from your fiddle :

    I have got the bounding box of the path to work out the X translation (get the end point of the path) :

     var translateWidth = document.getElementsByClassName("pathLine")[0].getBBox().width;
    

    And for the Y translation, I have passed the last value of the array to the yRange like so :

    yRange(lineData[lineData.length - 1])
    

    So now your text function is as follows :

    vis.append("text")
        .attr("transform", function(d) {
          return "translate(" + (translateWidth + 50) + "," + yRange(lineData[lineData.length - 1]) + ")"
        })
        .attr("text-anchor", "start")
        .style("fill", "black")
        .text("abc")
    

    Notice I changed the append from path to vis as you can't append text to SVG elements like you were trying to do.

    Updated fiddle : https://jsfiddle.net/thatOneGuy/dqch1s13/4/

    Working code if needed here :

    var vis = d3.select('#visualisation'),
      WIDTH = 1000,
      HEIGHT = 400,
      MARGINS = {
        top: 20,
        right: 20,
        bottom: 20,
        left: 50
      };
    
    var drawLine = function(data, color, index) {
      var lineData = data;
      var xRange = d3.scale.linear().range([MARGINS.left, WIDTH - MARGINS.right]).domain([0, 30]),
        yRange = d3.scale.linear().range([HEIGHT - MARGINS.top, MARGINS.bottom]).domain([_.min(lineData), _.max(lineData)]);
    
      var lineFunc = d3.svg.line()
        .x(function(d) {
          return xRange(lineData.indexOf(d));
        })
        .y(function(d) {
          return yRange(d);
        })
        .interpolate('basis');
      console.log(lineData[lineData.length - 1])
      var path = vis.append('path').attr('class', 'pathLine')
        .attr('d', lineFunc(lineData))
        .attr('stroke', function(d) {
          return color
        })
        .attr('stroke-width', 3)
        .attr('fill', 'none');
    
      var totalLength = path.node().getTotalLength();
    
      path
        .attr("stroke-dasharray", totalLength + " " + totalLength)
        .attr("stroke-dashoffset", totalLength)
        .transition()
        .duration(5000)
        .delay(index * 1000)
        .ease("linear")
        .attr("stroke-dashoffset", 0);
      console.log(path)
      var translateWidth = document.getElementsByClassName("pathLine")[0].getBBox().width;
    
      vis.append("text")
        .attr("transform", function(d) {
          return "translate(" + (translateWidth + 50) + "," + yRange(lineData[lineData.length - 1]) + ")"
        })
        .attr("text-anchor", "start")
        .style("fill", "black")
        .text("abc")
    };
    
    drawLine([1, 20, 3, 4, 15, 6, 7, 18, 9, 0, 12, 23, 24, 12, 100], 'red', 2);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
    <svg id="visualisation" width="1200" height="400" style="position: absolute;left: 50%;transform: translateX(-50%);"></svg>