D3.js Barchart timescale : Uneven space between bars - javascript

I guess the title explains the problem. Similar questions on SO refer to ordinal scales.
x axis scale:
var x = d3.time.scale()
.domain(d3.extent(data, function(d) {
return d.date;
}))
.range([width/data.length/2, width-(width/data.length/2)]);
The bars
bars.enter()
.append("rect")
.classed("column", true)
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
.attr("width", (width-(width/data.length)) / (data.length+2))
.attr("x", function(d) { return x(d.date) - (width/data.length)/2; })
.attr("y", height)
.attr("height", 0)
.transition().duration(1000)
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value);});
How can I amend this code so that I have a whole and equal number of pixels between each vertical bar, and thereby getting an even spacing ?

Related

Customize grid lines in d3 bar chart

I am developing a two-way bar chart using d3. I want to add grid lines to the bar chart, how to customize those grid lines. I've used d3fc to draw the grid lines. It looks something like
var x = d3.scaleLinear()
.rangeRound([0, width])
var y = d3.scaleBand()
.rangeRound([0, height]).padding(0.5);
var xAxis = d3.axisBottom(x)
.ticks(8)
.tickSize(0)
.tickFormat(function(d){
return d3.format('.00s')(Math.abs(d)); // Use Math.abs() to get the absolute value
});
var yAxis = d3.axisLeft(y)
.ticks(5)
.tickSize(0);
//draw grid lines
const gridline = fc.annotationSvgGridline()
.xScale(x)
.yScale(y);
var svg = d3.select("#ageSexNotif").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain([-d3.max(data, function(d){
return d.female
})*1.2,d3.max(data, function(d){
return d.female
})*1.2])
y.domain(data.map(function (d) {
return d.age;
}));
svg.append("g")
.attr("class", "x axis")
.call(xAxis)
.call(gridline);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// .call(gridline);
var barsRight = svg.selectAll(".bar")
.data(data)
.enter().append("g")
barsRight.append("rect")
.attr("class", "bar")
.attr("x", function (d) {
return x(Math.min(0, d.female));
})
.attr("y", function (d) {
return y(d.age);
})
.attr("width", function (d) {
return Math.abs(x(d.female) - x(0));
})
.transition()
.duration(1500)
.delay(200)
.style("margin-top", "10px")
.attr("height", y.bandwidth())
.attr("fill", "#F293C9")
.attr("text", "label");
barsRight.append("text")
.attr("class", "label")
//y position of the label is halfway down the bar
.attr("y", function (d) {
return y(d.age) + y.bandwidth()- 6;
})
//x position is 3 pixels to the right of the bar
.attr("x", function (d) {
return x(d.female) + 10;
})
.text(function (d) {
return (d.female/1000)+'k';
})
.style("font-family", "Source Sans Pro")
.style("font-size", "14px")
.style("font-weight", "bold")
.attr("fill", "#F293C9");
var barsLeft = svg.selectAll(".bar2")
.data(data)
.enter().append("g")
barsLeft.append("rect")
.attr("class", "bar2")
.attr("x", function (d) {
return x(Math.min(0, -d.male));
})
.attr("y", function (d) {
return y(d.age);
})
.attr("width", function (d) {
return Math.abs(x(-d.male) - x(0));
})
.transition()
.duration(1500)
.delay(200)
.style("margin-top", "10px")
.attr("height", y.bandwidth())
.attr("fill", "#4880FF");
barsLeft.append("text")
.attr("class", "label")
.style("font-family", "Source Sans Pro")
.style("font-size", "14px")
.style("font-weight", "bold")
.attr("fill","#4880FF")
//y position of the label is halfway down the bar
.attr("y", function (d) {
return y(d.age) + y.bandwidth()- 6;
})
//x position is 3 pixels to the right of the bar
.attr("x", function (d) {
return x(-d.male) - 40;
})
.text(function (d) {
return (d.male/1000)+'k';
});
The result of my chart is
My chart should look like this
How to join the edges in x-axis and highlight the base axis as shown in the image? Any help for customizing the grid lines is appreciated.
Link to my example link
Thanks in advance!
You can add class name to your grid lines using attr('class', 'class-name'), and add your effect by CSS.
I've made some changes to your pen that you can see here.
The main changes:
join the edges in x-axis
If you remove the *1.2 multiplier from the domains and add .nice() then the x-axis will be squared off with the gridlines.
var x = d3.scaleLinear()
.rangeRound([0, width])
.domain([-d3.max(data, d => d.female), d3.max(data, d => d.female)])
.nice();
highlight the base axis
We can do this using an annotation line from d3fc.
const line = fc.annotationSvgLine()
.xScale(x)
.yScale(y)
.orient('vertical')
.label('')
.decorate(selection => {
selection.select('line')
.attr('stroke', 'black');
});
svg.datum([0]).call(line);
Here we are creating an annotation line using our scales. We set the line to be vertical and we remove the default label by replacing it with an empty string. After that we use the decorate function to colour the line black.
customizing the gridlines
We can control the opacity of the gridlines using a decorate function.
const opacityDecorate = selection => {
selection.attr('opacity', 0.2);
};
const gridline = fc.annotationSvgGridline()
.xScale(x)
.yScale(y)
.xDecorate(opacityDecorate)
.yDecorate(opacityDecorate);
Here we are using a decorate function to set the opacity for the both the horizontal and vertical gridlines. Alternatively you could also use different decorate functions to style the horizontal and vertical lines differently.
I hope this helps!

d3js v5 Resize Grouped Bar Chart

I'm trying to resize a grouped bar chart with a resize() function.
function resize(){
width = parseInt(d3.select(".c_chart").style("width"), 10);
x0.rangeRound([margin.left, width-margin.right]);
x1.rangeRound([margin.left,x0.bandwidth()-margin.right])
yAxis.tickSize(width);
svg.selectAll("rect")
.attr("x", function(d) { return x1(d.key); })
.attr("width", x1.bandwidth());
svg.selectAll(".x_axis")
.call(xAxis)
.selectAll("text")
.call(wrap, x0.bandwidth());
}
When I start to resize the window, x-axis is ok but the x-position of my recent don't "follow" the ticks of my x-axis.
Then, I suspect that the problem is due to x- attribute but how can I fix that?
Here is my code: https://plnkr.co/edit/XEoM7lsBvZQmY87Wz1SP?p=preview
Add a class (gbar) to the g containing the group
svg
.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(" + x0(d.categorie) + ",0)"; })
.attr("class", "gbar")
.selectAll("rect")
.data(function(d) { return keys.map(function(key) { return {key: key, value: d[key]}; }); })
.enter().append("rect")
.attr("x", function(d) { return x1(d.key); })
.attr("y", function(d) { return y(d.value); })
.attr("width", x1.bandwidth())
.attr("height", function(d) { return height-margin.bottom - y(d.value); })
.attr("fill", function(d) { return z(d.key); });
In the resize function update the translation
svg.selectAll(".gbar")
.attr("transform", function(d) { return "translate(" + x0(d.categorie) + ",0)"; });
and update the size of the SVG
svg
.attr("width",width)
.attr("height",height);
Don't take the margin in the x1 scale
const x1 = d3.scaleBand()
.padding(0.05)
.domain(keys)
//.rangeRound([margin.left,x0.bandwidth()-margin.right])
.rangeRound([0,x0.bandwidth()]);
// resize()
//x1.rangeRound([margin.left,x0.bandwidth()-margin.right])
x1.rangeRound([0,x0.bandwidth()])
The only thing left to fix is the y-axis grid line,........

How to increase xaxis in d3js v4 bar chart?

I'm trying to learn how to code with the d3.js. I am trying to make a simple bar graph with this json file. I got stuck trying to format the xaxis in the file. I've tried looking at the d3.js API and I am still lost. I would be very grateful for any help.
Here is the result screenshot
This image is for shorter xaxis points
This output looks good
This output results when more data points in xaxis
Can anyone suggest me how to increase the xaxis length based on data point coiming to xaxis
Here is my code
.bar {
fill: #F39473;
}
.highlight {
fill: orange;
}
<!doctype html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<svg width="900" height="500"></svg>
<script>
var svg = d3.select("svg"),
top= 20, right= 20, bottom= 50, left= 70,
margin = 200,
width = svg.attr("width") - margin,
height = svg.attr("height") - margin;
var x = d3.scaleBand().range([0, width]).padding(0.4),
y = d3.scaleLinear().range([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + 100 + "," + 100 + ")");
d3.json("data.php", function(error, data) {
data.forEach(function(d) {
d.date = (d.date);
d.count = +d.count;
})
x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.count; })]);
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.append("text")
.attr("y", height - 250)
.attr("x", width - 100)
.attr("text-anchor", "middle")
.attr("stroke", "black")
.text("date");
g.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("count");
g.append("g")
.call(d3.axisLeft(y).tickFormat(function(d){
return d;
}).ticks(10))
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.on("mouseover", onMouseOver) //Add listener for the mouseover event
.on("mouseout", onMouseOut) //Add listener for the mouseout event
.attr("x", function(d) { return x(d.date); })
.attr("y", function(d) { return y(d.count); })
.attr("width", x.bandwidth())
.transition()
.ease(d3.easeLinear)
.duration(400)
.delay(function (d, i) {
return i * 50;
})
.attr("height", function(d) { return height - y(d.count); });
});
//mouseover event handler function
function onMouseOver(d, i) {
d3.select(this).attr('class', 'highlight');
d3.select(this)
.transition() // adds animation
.duration(400)
.attr('width', x.bandwidth() + 5)
.attr("y", function(d) { return y(d.count) - 10; })
.attr("height", function(d) { return height - y(d.count) + 10; });
g.append("text")
.attr('class', 'val')
.attr('x', function() {
return x(d.date);
})
.attr('y', function() {
return y(d.count) - 15;
})
.text(function() {
return [ +d.date, +d.count]; // Value of the text
});
}
//mouseout event handler function
function onMouseOut(d, i) {
// use the text label class to remove label on mouseout
d3.select(this).attr('class', 'bar');
d3.select(this)
.transition() // adds animation
.duration(400)
.attr('width', x.bandwidth())
.attr("y", function(d) { return y(d.count); })
.attr("height", function(d) { return height - y(d.count); });
d3.selectAll('.val')
.remove()
}
</script>
</body>
</html>
I would use d3.nest() and make a rollup out of the key value you'd want to count (d.date in this case) and use this count value in your width variable.
Here's a plunker I made using this method.

D3.js: Uniformly spaced bars on time scale

I am trying to create a simple bar chart with evenly spaced bars on time scale in x-axis but unsuccessful even with the solution given here
I am getting overlaps in the bars and unevenly spaced bars. Part of my code as below:
var x = d3.time.scale().range([width/data.length/2, width-width/data.length/2]);
var y = d3.scale.linear().range([height, 0]);
bars.transition().duration(1000)
.attr("x", function(d) { return x(d.date) - Math.floor(width/data.length/2); })
.attr("width", Math.floor(width / data.length))
.attr("y", function(d) { return y(d.c); })
.attr("height", function(d) { return height - y(d.c);});
bars.enter().append("rect")
.attr("class", "air_used")
.attr("width", Math.floor(width / data.length))
.attr("x", function(d) { return x(d.date) - Math.floor((width/data.length)/2); })
.attr("y", height)
.attr("height", 0)
.transition().duration(1000)
.attr("y", function(d) { return y(d.c); })
.attr("height", function(d) { return height - y(d.c);});
JSFiddle here: http://jsfiddle.net/vivekratnavel/v7rx8ku9/2/
Any help is appreciated. Thanks!
set hours to 0hrs:0min:0sec before scaling the time on x-axis.
http://jsfiddle.net/hnj30m4j/
bars.transition().duration(1000)
.attr("x", function(d) { return x(d.date.setHours(0,0,0)) - Math.floor(width/data.length/2); })
.attr("width", Math.floor(width / data.length))
.attr("y", function(d) { return y(d.c); })
.attr("height", function(d) { return height - y(d.c);});
bars.enter().append("rect")
.attr("class", "air_used")
.attr("width", Math.floor(width / data.length))
.attr("x", function(d) { return x(d.date.setHours(0,0,0)) - Math.floor((width/data.length)/2); })
.attr("y", height)
.attr("height", 0)
.transition().duration(1000)
.attr("y", function(d) { return y(d.c); })
.attr("height", function(d) { return height - y(d.c);});

d3.js barchart plugin animation methods

I am having issues trying to build this jquery based d3.js barchart plugin.
the bars are displaced to the left, not sure why
the bars are not updating to new data.
I've tried to get the bars to animate - but not had any success.
http://jsfiddle.net/NYEaX/161/
Here is the animate bars function
animateBars: function(data){
var svg = d3.select(methods.el["selector"] + " .barchart");
var bars = svg.selectAll(".bar")
.data(data);
bars
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return methods.x(d.letter); })
.attr("width", methods.x.rangeBand())
.attr("y", function(d) { return methods.y(d.frequency); })
.attr("height", function(d) { return methods.height - methods.y(d.frequency); })
.transition()
.duration(300)
bars
.transition()
.duration(300)
bars.exit()
.transition()
.duration(300)
}
I fixed the transition for new bars in your jsfiddle. I hope it reveals the functionality of transitions:
http://jsfiddle.net/NYEaX/893/
How it works: After it sets the initial height and y value, it adds a transition to the end height and y value.
bars
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return methods.x(d.letter); })
.attr("width", methods.x.rangeBand())
.attr("y", function(d) { return methods.y(0); })
.attr("height", function(d) { return methods.height - methods.y(0); })
.transition().duration(1000)
.attr("y", function(d) { return methods.y(d.frequency); })
.attr("height", function(d) { return methods.height - methods.y(d.frequency); });

Categories

Resources