endkugelfang endkugelfang - 1 month ago 7
Javascript Question

drawing a specific number of rects (d3.js)

I'm trying to draw a specific number of rectangles with different widths, next to each other. When any rectangle hits the maximum width of my svg element (e.g. 500), it should continue in the next line. Is it possible to solve this in a simple way?

Output - Image
This is, what I got so far. The problem is, firebug shows, that my last width (570) is to big for my svg.

<script>
var width = 500;
var height = 500;
var rectHeight = 20;
var xPos = 0;
var xWidthOld = 0;
var rectSpace = 0.5;
var dataArray = [20, 78, 40, 60, 570];

var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);

rects = d3.select("svg").selectAll("rect")
.data(dataArray)
.enter()
.append("rect")
.attr("width", function(d) { return d; })
.attr("height", rectHeight)
.attr("x", function(d, i) {
return getPos(d, i);
});


function getPos(rectWidth, index) {
if(index != 0)
xPos = xPos + xWidthOld + rectSpace;

xWidthOld = rectWidth;
return xPos;
}
</script>


EDIT - New Picture - Continue new Line

Answer

I used two different algorithms. The first is calculating the position of each node on D3 cycle, the second is calculating the positions in advance via initData function.

Here is what I have:

When it overflows, it jumps a line

var width = 500;
var height = 500;
var rectHeight = 20;
var posX = 0;
var posY = 0;
var rectSpace = 0.5;
var dataArray = [20, 78, 40, 60, 370, 42];

var svg = d3.select("body").append("svg")
  .attr("width", width)
  .attr("height", height);

rects = d3.select("svg").selectAll("rect")
  .data(dataArray)
  .enter()
  .append("rect")
  .attr("width", function(d) {
    return d;
  })
  .attr("height", rectHeight)
  .attr("x", function(d, i) {
    return getPosX(d, i);
  })
  .attr("y", function(d, i) {
    return getPosY(d, i);
  });

// Calculate PosX
function getPosX(rectWidth, index) {
  // If rect goes out of the SVG, restart at posX = 0
  if (posX + rectWidth > width) {
    posX = 0;
  }
  newPosX = posX; // Return this position
  posX += rectWidth + 1; // prepare position for next node
  return newPosX;
}

// Calculate PosY
function getPosY(rectWidth, index) {
  // reset posX at first calculation of posY
  if (index == 0) {
    posX = 0;
  }
  // If rect goes out of the SVG increment Y position of rect
  if (posX + rectWidth > width) {
    posY += rectHeight + 1;
    posX = 0;
  }
  posX += rectWidth + 1; // Calculate position for next node
  return posY;
}
svg {
  border: 1px solid gray;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

JSFiddle

When it overflows, the node is cut and the rest is added to a new line

var width = 500;
var height = 500;
var rectHeight = 20;
var rectSpace = 0.5;
var dataArray = [20, 78, 40, 60, 1370, 42];
var nodes = [];

var svg = d3.select("body").append("svg")
  .attr("width", width)
  .attr("height", height);

rects = d3.select("svg").selectAll("rect")
  .data(initData(dataArray))
  .enter()
  .append("rect")
  .attr("width", function(d) {
    return d.value;
  })
  .attr("height", rectHeight)
  .attr("x", function(d, i) {
    return d.x;
  })
  .attr("y", function(d, i) {
    return d.y;
  });

// Build displayed data below
function initData(data) {
  var posX = 0;
  var posY = 0;

  // Claculate position of each node
  for (var i in data) {
    var node = addNode(data[i], posX, posY);

    // If there is an overflow
    if (node.x + node.value > width) {
      var overflowValue = node.x + node.value - width;

      // Add nodes until there is no more overflow
      while (overflowValue > 0) {
        // Update current node value
        node.value = width - node.x;
        // Calculate new node posX and posY
        posX = 0;
        posY += rectHeight + 1;
        node = addNode(overflowValue, posX, posY);
        // Claculate new overflow
        overflowValue = node.x + node.value - width;
      }
    }
    posX += node.value + 1;
  }

  return nodes;
}

function addNode(value, x, y) {
  var newNode = {
    value: value,
    x: x,
    y: y
  };

  nodes.push(newNode);

  return newNode;
}
svg {
  border: 1px solid gray;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

JSFiddle