I'm trying to make a d3 Bar Graph animate from the x axis up upon load. Here is my code so far:
$(document).ready(function(){
setTimeout(function(){
var t = 0, // start time (seconds since epoch)
v = 70, // start value (subscribers)
data = d3.range(4).map(next); // starting dataset
function next() {
return {
value: v = dataArray[t].length,
time: ++t
};
}
var w = 30,
h = 80;
var x = d3.scale.linear()
.domain([0, 1])
.range([0, w]);
var y = d3.scale.linear()
.domain([0, 100])
.rangeRound([0, h]);
var barPadding = 5;
var chart = d3.select("#revenue").append("svg")
.attr("class", "chart")
.attr("width", w * data.length + (data.length-1)*8)
.attr("height", h);
var rects = chart.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d, i) { return i*35; })
.attr("y", function(d) { return h - y(d.value) - .5; })
.attr("width", w)
.attr("fill", "white")
.attr("height", function(d) { return 0; });
rects.data(data)
.transition().duration(2000).delay(200)
.attr("height", function(d) { return y(d.value); })
.attr("y", function(d) {return h-y(d.value); });
},2000);
});
I know the answer lies within animating the Y position at the same time so that it gives the appearance of growing upwards, but no matter what I change, I can't get it to animate from the bottom up.
graph.selectAll("rect")
.append("animate")
.attr("attributeName","y")
.attr("attributeType","XML")
.attr("begin","0s")
.attr("dur","1s")
.attr("fill","freeze")
.attr("from",yaxis_offset)
.attr("to",function(d){return yscale(d.value);} );
graph.selectAll("rect")
.append("animate")
.attr("attributeName","height")
.attr("attributeType","XML")
.attr("begin","0s")
.attr("dur","1s")
.attr("fill","freeze")
.attr("from",0)
.attr("to",function(d){return yaxis_offset-yscale(d.value);});
This is the code which worked for me to animate the barchart as you said... change the variable according to your need..
Related
Can anyone tell me what I am doing wrong? I am getting an error.
Uncaught (in promise) TypeError: xScale.bandwidth is not a function
at barChart (bar_chart.js:53:27)
at bar_chart.js:84:5
I am trying to create a bar graph of this data.
year,total_ghg
2000,661.97
2001,665.72
2002,660.75
2003,583.65
2004,635.5
2005,598.44
2006,646.91
2007,646.46
2008,617.09
2009,633.8
2010,601.14
2011,644.74
2012,643.12
2013,555.26
2014,566.21
2015,566.47
2016,577.32
2017,623.08
2018,619.26
my js
var dataset;
function barChart(dataset) {
//declaring Varibales
var margin = {top:50, right:50, bottom:50, left:50};
var width = 500-margin.left-margin.right;
var height = 500-margin.top-margin.bottom;
//creating svg
var svg = d3
.select("#barchart")
.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 + ")");
//setting up scales
var xScale = d3
.scaleTime()
.domain([
d3.min(dataset, function (d) {
return d.year;
}),
d3.max(dataset, function (d) {
return d.year;
}),
])
.range([0,width]);
var yScale = d3.scaleLinear()
.domain([0,d3.max(dataset, function (d) {
return d.total_ghg;
})
])
.range([height,0]);
// Plotting axis
svg.append("g").attr("transform", "translate(0," + height + ")").call(d3.axisBottom(xScale));
svg.append("g").call(d3.axisLeft(yScale));
//Set up groups
svg.selectAll("mybar")
.enter()
.data(dataset).append("rect")
.attr("x", function(d) {
return xScale(d.year);
})
.attr('y', function(d) {
return yScale(d.total_ghg);
})
.attr("width", xScale.rangeBand())
.attr('height', function(d) {
return height - yScale(d.total_ghg);
})
.attr("fill", "#004DA5")
.on("mouseover", function(event, d) {
d3.select(this).attr("fill", "orange");
var xPosition = parseFloat(d3.select(this).attr("x")) + xScale.bandwidth() / 2 - 5;
var yPosition = parseFloat(d3.select(this).attr("y")) + 20;
svg.append("text")
.attr("id", "tooltip")
.attr("x", xPosition)
.attr("y", yPosition)
.text(d);
}).on("mouseout", function(d) {
d3.select("#tooltip").remove();
d3.select(this)
.attr("fill", "#004DA5")
});
}
function init() {
d3.csv("src/data/total_ghp.csv", function (d) {
// + : force the year and month to be typed as a number instead of string
return {
year: d3.timeParse("%Y")(d.year),
total_ghg: +d.total_ghg,
};
}).then(function (data) {
dataset = data;
barChart(dataset);
});
}
window.addEventListener("load", init);
Any suggestions Please
What I have tried
ScaleOrdinal
rangeBandRounds
RangeBand()
instead of bandwidth
and a few more things like using a different d3 script same error in every scenario
Your xScale uses scaleTime, which is meant for charts where the axis represents time (i.e. line charts). For bar charts you should use scaleBand instead which does have the bandwidth function:
const xScale = d3
.scaleBand()
.domain([
d3.min(dataset, (d) => d.year),
d3.max(dataset, (d) = > d.year),
])
.range([0, width]);
More information on scaleBand can be found here: https://observablehq.com/#d3/d3-scaleband
There are some other mistakes in your code that prevent your bars from rendering:
Replace scaleTime with scaleBand for xScale
Replace xScale.rangeBand() with xScale.bandwidth()
Move .enter() to come after .data(dataset)
I'm trying to create a bar spiral in d3 and I'm using this resource: http://bl.ocks.org/larsenmtl/222043d93a41d48b58d2bfa1e3d4f708
I'm not getting an error, but also I'm just getting a blank page no chart at all. Any guidance on where I'm going wrong would be appreciated. Also the console.table isn't showing data, even though initially it did.
// reading in the data
const dataset = d3.csv("/Journalists_Death.csv").then(function(data) {
console.log(data[0]);
});
//creating spiral chart
var width = 500,
height = 500,
start = 0,
end = 2.25,
numSpirals = 4;
var theta = function(r) {
return numSpirals * Math.PI * r;
};
var r = d3.min([width, height]) / 2 - 40;
var radius = d3.scaleLinear()
.domain([start, end])
.range([40, r]);
var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("tranform", "translate(" + width / 2 + ","+ height / 2 +")");
// create the spiral, borrowed from http://bl.ocks.org/syntagmatic/3543186
var points = d3.range(start, end + 0.001, (end = start) / 1000);
var spiral = d3.radialLine()
.curve(d3.curveCardinal)
.angle(theta)
.radius(radius);
var path = svg.append("path")
.datum(points)
.attr("id", "spiral")
.attr("d", spiral)
.style("fill", "none")
.style("stroke", "steelblue");
//fudge some data, 2 years of data starting today
var spiralLength = path.node().getTotalLength(),
N = 730,
barWidth = (spiralLength / N) - 1;
year = d => d.year
Total = d => d.Total
//here's our time scale that'll run along the spiral
var timeScale = d3.scaleTime() //line 52
.domain(d3.extent(dataset, function(d){
return d.year;
}))
.range([0, spiralLength]);
//yScale for the bar height
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d){
return d.Total
})])
.range([0, (r/numSpirals) - 30]);
//append our rects
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d,i){
//placement calculations
var linePer = timeScale(d.year),
posOnLine = path.node().getPointAtLength(linePer),
angleOnLine = path.node().getPointAtLength(linePer - barWidth);
d.linePer = linePer; // % of distance are on the spiral
d.x = posOnLine.x; // x position on the spiral
d.y = posOnLine.y; // y on position on the spiral
d.a = (Math.atan2(angleOnLine.y, angleOnLine.x) * 180 / Math.PI) - 90;
return d.x;
})
.attr("y", function(d){
return d.y;
})
.attr("width", function(d){
return barWidth;
})
.attr("height", function(d){
return yScale(d.Total);
})
.style("fill", "steelblue")
.style("stroke", "none")
.attr("transform", function(d){
return "rotate(" + d.a + "," + d.x + "," + d.y + ")";
});
I created a simple barchart with d3.js. My problem my complete chart is not shown but it is cut off to the right. Only 16 of the 20 bars are shown
I guess it is a scaling issue but I don't know how to fix it. If I increase the width it shows me more bars, but I'd like to keep the original width. Here is my code:
{#Creating a barchart#}
var dataset = [133,131,111,345,665,665,454,44,4,235....]; //These are the bars
var svgWidth = 900, svgHeight = 400, barPadding = 10;
var barWidth = (svgWidth / dataset.length * 2 );
var svg = d3.select('svg')
.attr("width", svgWidth)
.attr("height", svgHeight);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset)])
.range([0, svgHeight]);
var barChart = svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("y", function(d) {
return svgHeight - yScale(d)
})
.attr("height", function(d) {
return yScale(d);
})
.attr("width", barWidth - barPadding)
.attr("transform", function (d, i) {
var translate = [barWidth * i, 0];
return "translate("+ translate +")";
});
var text = svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {
return d;
})
.attr("y", function(d, i) {
return svgHeight - d - 2;
})
.attr("x", function(d, i) {
return barWidth * i;
})
.attr("fill", "black");
</script>
Any help is highly appreciated!! Thanks in advance!
Try removing the multiplication by 2 in your barWidth formula
var barWidth = svgWidth / dataset.length;
I have a series of paired xy coordinates that create 58 lines. I want to plot them on a Cartesian graph, values are between -5 and 5 on both axis, essentially making a scatter plot of lines. I have made something similar in matplotlib using the quiver function, but I want to be able to do this in D3. I would also like to be able to label each line, or each line that meets a length threshold. The code I have come up with below. Thanks.
var lisa = [["Eloy",0.0169808,-0.695317,-0.0510301,-0.6995938],
["Florence",-0.3465685,-0.6790588,-0.5869514,-0.6762134],
["Phoenix",0.677068,-0.5754814,-0.6052215,-0.6158059],
["Tucson",-0.663848,0.4111043,-0.6722116,0.011639]]
var w = 200;
var h = 200;
//create the svg element and set the height and width parameters
var svg = d3.select("div").select("div")
.append("svg")
.attr("height",h)
.attr("width", w)
.style("border", "1px solid black");
//Create the scale for the scatter plot
var xScale = d3.scale.linear()
.domain([d3.min(dataset, function(d) { return d[0];}),d3.max(dataset, function(d) { return d[0];})])
.range([-1,1]);
var yScale = d3.scale.linear()
.domain([d3.min(dataset, function(d) { return d[1];}),d3.max(dataset, function(d) { return d[1];})])
.range([-1,1]);
//This is the function that creates the SVG lines
var line = svg.selectAll("line")
.data(lisa)
.enter()
.append("line");
//This gets the cooresponding x,y cordinates from the dataset
line.attr("x1", function(d) {
return xScale(d[0]);
})
.attr("y1", function(d) {
return yScale(d[1]);
})
.attr("x2", function(d) {
return xScale(d[2]);
})
.attr("y2", function(d) {
return yScale(d[3]);
})
.attr("stroke", "black");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Your code has some problems:
First, your range right now ([-1, 1]) makes no sense. This should be the domain instead (I changed the ranges to [0, w] and [0, h]).
In your real code, the domain should be [-5, 5] and the range should be the limits of the plot, something like [leftLimit, rightLimit] and [topLimit, bottomLimit] (have in mind that, in an SVG, the 0 position for the y axis is the top, not the bottom).
Second, given this array:
["Tucson",-0.663848,0.4111043,-0.6722116,0.011639]
your x and y positions should be the indices 1,2,3 and 4, not 0, 1, 2 and 3.
Besides that changes, I added the labels:
var text = svg.selectAll(".text")
.data(dataset)
.enter()
.append("text");
text.attr("font-size", 10)
.attr("x", function(d) {
return xScale(d[1]);
})
.attr("y", function(d) {
return yScale(d[2]);
})
.text(d => d[0]);
Here is the demo with the corrections:
var dataset = [["Eloy",0.0169808,-0.695317,-0.0510301,-0.6995938],
["Florence",-0.3465685,-0.6790588,-0.5869514,-0.6762134],
["Phoenix",0.677068,-0.5754814,-0.6052215,-0.6158059],
["Tucson",-0.663848,0.4111043,-0.6722116,0.011639]];
var color = d3.scale.category10();
var w = 400;
var h = 300;
//create the svg element and set the height and width parameters
var svg = d3.select("body")
.append("svg")
.attr("height",h)
.attr("width", w)
.style("border", "1px solid black");
//Create the scale for the scatter plot
var xScale = d3.scale.linear()
.domain([-1,1])
.range([0,w]);
var yScale = d3.scale.linear()
.domain([-1,1])
.range([0,h]);
//This is the function that creates the SVG lines
var line = svg.selectAll("line")
.data(dataset)
.enter()
.append("line");
//This gets the cooresponding x,y cordinates from the dataset
line.attr("x1", function(d) {
return xScale(d[1]);
})
.attr("y1", function(d) {
return yScale(d[2]);
})
.attr("x2", function(d) {
return xScale(d[3]);
})
.attr("y2", function(d) {
return yScale(d[4]);
})
.attr("stroke-width", 2)
.attr("stroke", (d,i)=>color(i));
var text = svg.selectAll(".text")
.data(dataset)
.enter()
.append("text");
text.attr("font-size", 10)
.attr("x", function(d) {
return xScale(d[1])+2;
})
.attr("y", function(d) {
return yScale(d[2]) + 4;
})
.text(d=>d[0]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I want to modify the basic side bar chart to be a stack bar chart, which will reflect partial-amount:total relationship. I already created a matrix with the following:
[{y:0, x0:221, x1:1670},
{y:1, x0:581, x1:1473},
{y:2, x0:2485, x1:2643},
{y:3, x0:135, x1:8714},
{y:4, x0:31, x1:211}]
For reference, in each case the true total would be x0 + x1.
I have a normal bar chart for the totals, but I cannot understand how to convert it to a stacked chart. Also, if there is a way to accomplish this without mutating the data (subtracting x0 from the true total to get x1), that would also be ideal.
Existing BarChart
// Constants
var width = 450,
barHeight = 20,
height = 300,
padding = 10,
leftMargin = 10;
var typeBarChart = d3.select('.typeBarChart')
.attr('width', width)
.attr('height', barHeight*dataGroupByType.length); // dataGroupByType is a D3 nest data series with length is 5
// X-axis;
var x = d3.scale.linear()
.domain([0, maxCrime]) // maxCrime determine elsewhere, approx. 8850
.range([0, width]);
var chart = d3.select(".typeBarChart")
.attr("width", width)
.attr("height", barHeight * dataGroupByType.length);
var bar = chart.selectAll("g")
.data(dataGroupByType)
.enter()
.append("g")
.attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });
bar.append("rect")
.attr("width", function(d) { return x(d.values); })
.attr("height", barHeight - 1);
bar.append("text")
.attr("x", function(d) {
if (d.values < 1000) {
return x(d.values) + 20;
} else {
return x(d.values) - 3;
}
})
.attr("y", barHeight / 2)
.attr("dy", ".35em")
.text(function(d) { return d.values; });
There are a some issues with the code template....
I refined few to create a simplest stacked bar graph.. this would help you get started...
Working fiddle: https://jsfiddle.net/egmf47ne/
CODE:
HTML
<div class ="typeBarChart"></div>
JS
var _data = [{y:0, x0:221, x1:1670},
{y:1, x0:581, x1:1473},
{y:2, x0:2485, x1:2643},
{y:3, x0:135, x1:8714},
{y:4, x0:31, x1:211}]
// Constants
var width = 450,
barHeight = 20,
height = 300,
padding = 10,
leftMargin = 10;
var typeBarChart = d3.select('.typeBarChart')
.append('svg')
.attr('width', width)
.attr('height', barHeight*_data.length);
var x = d3.scale.linear()
.domain([0, 8850]) // maxCrime
.range([0, width]);
var bar = typeBarChart.selectAll("g")
.data(_data)
.enter()
.append("g")
.attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });
bar.append("rect")
.attr("fill","blue") // blue bars of x0 + x1
.attr("width", function(d) { return x(d.x0 + d.x1); })
.attr("height", barHeight - 1);
bar.append("rect")
.attr("fill","red") // red bars of x0 only
.attr("width", function(d) { return x(d.x0); })
.attr("height", barHeight - 1);