D3.js bar graph scale/range problem. Bar graph too low - javascript

I've completed a bar graph codepen showing US GDP (the popular one from FCC).
https://codepen.io/le-hu/pen/Baoywgd
The problem i can't eradicate is such - the graph does not start at bottom padding 50units above svg bottom, but instead spans to the border and even goes below svg border i believe. I've tried changing every setting. My bet is something's wrong with the yScale domain, or range property.
const yScale = d3.scaleLinear()
.domain([0,
d3.max(data.data, d => d[1])])
.range([height - padding, padding]);
Code seams to be ignoring the height - padding part in range setting. (or my understanding of it)
I'd like the graph to start on the line the x axis we have, the one showing dates in years 1950+.
Just like in the original:
https://codepen.io/freeCodeCamp/pen/GrZVaM
thank you for any insight!
Mehdi's solution worked like a charm - thank you very much for your time!

In SVG, the vertical coordinates go from top to bottom(reference).
This makes the reasoning about the y coordinate and height of a vertical bar in a bar chart less straightforward.
The tutorial Let's make a bar chart, part 4 explains how to perform this.
the y position of the rectangle has correctly been set tothe one of the value d[1]:
.attr("y", (d, i) => yScale(d[1]))
The height of the rectangle is incorrect, though. It should be the distance between origin (value 0) and position of the value d[1]. As shown below:
.attr("height", (d, i) => yScale(0)- yScale(d[1]))

Related

Issue with rectangles not drawing equal with yaxis labels in d3v4

I am new to d3v4 and working on a chart where i need to show little rectangle on certain date matching to its title on yaxis. The problem i am facing is rectangles in the chart area not drawing equal to the yaxis point labels, i have tried changing the y value by hardcoding, it works fine but the point is the number of data object will change in real time like it could be any number of objects in an array. Here is the plunker
To draw the graph dynamically with limited data objects i've created few buttons on top of chart so that rectangles in the chart can draw equal to y-axis labels.
Any help is much appreciated.
You are using a band scale: that being the case, you should not change the y position, which should be just...
.attr('y', function(d) {
return yScale(d.title);
})
.. and you should not hardcode the height: use the bandwidth() instead:
.attr('height', yScale.bandwidth())
The issue now is setting the paddingInner and paddingOuter of the scale until you have the desired result. For instance:
var yScale = d3.scaleBand().domain(data.map(function(d) {
return d.title
}))
.range([height - 20, 0])
.paddingInner(0.75)
.paddingOuter(.2);
Here is the plunker with those changes: https://plnkr.co/edit/ZxGCeDGYwDGzUCYiSztQ?p=preview
However, if you still want (for whatever reason) hardcode the height or the width of the rectangles, use a point scale instead, and move the y position by half the height.

How to properly add padding to the plot area in a D3 line chart?

I have a line chart in D3 as seen here:
I am attempting to extend the x-axis to be the same size as the y-axis tick width. Currently I am setting the ranges as follows:
// set the ranges
var x = d3.scaleTime().range([20, width - 20]);
var y = d3.scaleLinear().range([height, 0]);
This achieves my desired effect of pushing the plot in from the left and right sides but does not extend the x-axis. I'm sure there has to be a way to add padding to the plot without changing the ranges but I can't seem to figure out how?
There's no easy way to add padding to a linear scale.
However, in your case, since your x axis is presenting categorical data (days of the week), you can use a d3.scalePoint and configure its outer padding with the padding function.

D3 bar chart left aligned x-axis

How do I retain the x-axis as 100% of the container width while maintaining left aligned ticks with some padding.
this.xAxis = d3.scaleBand()
.range([0, this.barWidth * this.data.length], 1);
If I change the range to .range([0, this.svgWidth]), the width is correct but my ticks are evenly spread out which is not my desired outcome.
The second and third arguments are not accepted anymore in range when you use a band scale. Instead, you'll have to use paddingInner and paddingOuter.
So, change your snippet to this:
this.xAxis = d3.scaleBand()
.range([0, this.svgWidth])
.paddingInner(someValue)
.paddingOuter(someValue);
Tweak with someValue until you find the desired outcome.

d3.js: show only part of data on xAxis

I'm using a linear scale chart with zoom/pan functionality to display a large dataset (500+ points). Here's the code I use to construct the x-scale:
x = d3.scale.linear()
.domain([0, data.length-1])
.range([0, w]);
This way all data is squeezed into the chart making it impossible to view the details like in the top part of the image:
I'd like to display the data similar to the bottom chart (and let the user scroll to see more of the data using the pan functionality).
One way to do this is to manipulate the domain of the X scale such that it maps the zoom boundaries to be the min/max domain values that map to the 0-width values of the range. You can then use a clip path to clip/hide that parts of the plot that are drawn outside of the X scale range.
It might make more sense with a jsFiddle example: http://jsfiddle.net/reblace/8TmM9/
In this example, there are 10 squares that are always being drawn. You can inspect the dom to see what I'm talking about. But, there is a clip path that is only wide enough for you to see 4 of them at a time. The X scale maps the input values of 0-9 to the output coordinates. The range is set as 0 to the width required to draw all 10 of the squares, and the domain is set as [0, 9].
var xScale = d3.scale.linear()
.domain(d3.extent(data))
.range([0, width]);
...
var svg = d3.select('#chart').append('svg')
.attr('width', clipWidth)
.attr('height', clipHeight);
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", clipWidth)
.attr("height", clipHeight);
...
var g = svg.append("g");
g.selectAll("rect")
.data(data).enter().append('rect')
.attr("class", "area").attr("clip-path", "url(#clip)")
.attr('x', xScale)
.attr('width', rectWidth)
.attr('height', rectHeight)
.style('fill', d3.scale.category20());
Initially, this will draw the first four rectangles in the visible pane. By manipulating the domain so that it is instead [1,10] or [2,11] or even [-1, 8], we can effectively shift the drawn elements left and right so that a different span of the plot is drawn in the visible area.
// Pan Left
xScale.domain([xScale.domain()[0] - 1, xScale.domain()[1] - 1]);
// Pan Right
xScale.domain([xScale.domain()[0] + 1, xScale.domain()[1] + 1]);
This technique is identical whether you are doing it with squares or plots.
Mike Bostock has an example that does this with plots in the manner you are attempting here as well: Focus+Context http://bl.ocks.org/mbostock/1667367

How to transition D3 axis without tick text attribute reset?

I have a D3 project where I'm drawing a time axis along the left side of the screen. I want to have it smoothly transition on window resize so I'm using D3 transitions. However the axis setup appears to be changing the "dy" attribute on the tick labels immediately causing the tick labels to jump downward and then transition back into their normal place any time the SVG is transitioned. Is there any way to set the "dy" attribute of the tick text as part of the axis call or a better way to transition?
My initial (one-time) axis setup:
var timeScale = d3.time.scale().domain([minTime, maxTime]);
var yAxis = d3.svg.axis().scale(timeScale).tickFormat(d3.time.format("%-m/%-d %-I:%M%p")).orient("right");
I have a function to update/transition the SVG elements I'm using. The first time the SVG is drawn init is set to true, false afterwards.
function updateSVG(init) {
...
timeScale.rangeRound([topPadding, svgHeight]);
// Use a transition to update the axis if this is an update
var t = (init) ? svgContainer : svgContainer.transition().duration(750);
// {1}: Create Y axis
t.select("g.axis").call(yAxis);
// {2}: Move Y axis labels to the left side
t.selectAll("g.tick > text")
.attr("x", 4)
.attr("dy", -4);
...
}
On an update at {1} tick labels all have a "dy" attribute of "-4" from the previous attr() call. At {2} applying the axis resets the "dy" attribute of these elements to a default of ".32em" after which they transition slowly back to "-4" causing them to jitter up and down as the window is resized and the axis is redrawn.
Here is a working JSFiddle that demonstrates the jump on the y-axis when the Result box is resized, resize just by a few pixels and it should be obvious: http://jsfiddle.net/YkDk4/1/
Just figured this out. By applying a "transform" attribute instead of a "dy" attribute the axis call() does not overwrite the value. So:
t.selectAll("g.tick > text")
.attr("x", 4)
.attr("dy", -4);
becomes:
t.selectAll("g.tick > text")
.attr("x", 4)
.attr("transform", "translate(0,-4)");
and everything transitions smoothly.
According to the bug fix made in response to this problem with the text-anchor attribute:
How to tweak d3 axis attributes when transition is present?
It looks like the dy attribute is supposed to update immediately during transitions...but it's not.
In any case, the easiest solution is simply to take the dy update OUT of the transition and apply it directly:
t.select(".y")
.call(yAxis);
chartSvg.selectAll(".y g.tick > text")
.attr("dy", -4);
That should avoid the "bounce".

Categories

Resources