Search code examples
d3.jscallmodulo

In D3js, how do I cumulative add on every nth bound data value?


I have this code which outputs a straight line composed of a bunch of line segments:

var width = 400;
var height = 100;
var data = [1,1,1,1,1,1,1,1,1,1];
var lineSegments = data.length + 1;

d3.select("body").append("svg")
        .attr({
            width: width,
            height: height
        })
    .selectAll("line")
    .data(data)
    .enter()
        .append("line")
            .attr({
                x1: function (d, i) { return i * (width / lineSegments); },
                x2: function (d, i) { return (i + 1) * (width / lineSegments); },
                y1: function (d, i) { return height / 2; },
                y2: function (d, i) { return height / 2; },
                stroke: "black",
                "stroke-width": 2
            });

I want every 3d segment to be offset in the y direction by, say 10px, and I need the offset to be cumlative, i.e. the 3d element should have offset 10px, the 6th element should be offset 20px, etc.

This should result in a line like this:

enter image description here

How should I modify the code to get it to work? Is there a special d3 way of doing this?


Solution

  • You can use d3's selection.each() to target every 3d segment. This method has some useful variables for performing various functions.

    line.each(function(d, i) {
      console.log(i); // prints the current index
      console.log(d); // prints the data at the current index
      console.log(this); // prints the current DOM element
    }
    

    In your specific case, the i variable is useful to you. You can use this inside of an if statement to change the attributes of every 3rd element. Your revised code might be...

    var width = 400;
    var height = 100;
    var data = [1,1,1,1,1,1,1,1,1,1];
    var lineSegments = data.length + 1;
    
    var offset = 5;
    
    d3.select("body").append("svg")
            .attr({
                width: width,
                height: height
            })
        .selectAll("line")
        .data(data)
        .enter()
            .append("line")
                .attr({
                    x1: function (d, i) { return i * (width / lineSegments); },
                    x2: function (d, i) { return (i + 1) * (width / lineSegments); },
                    y1: function (d, i) { return height / 2; },
                    y2: function (d, i) { return height / 2; },
                    stroke: "black",
                    "stroke-width": 2
                })
                .each(function(d,i) {
                    if (i !== 0 && i % 3 === 0) {
                       d3.select(this).attr('transform', 'translate(0,' + offset + ')');
                       offset += 5;
                    }
                })
    

    Here is a working codepen.