endkugelfang - 1 year ago 63
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

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;
// Claculate new overflow
overflowValue = node.x + node.value - width;
}
}
posX += node.value + 1;
}

return nodes;
}

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