Unable to add transition in d3 - javascript

I want to add transition effect on vertical bar I designed in d3. I am new to d3,i have tried adding transition() method but didn't quiet work. My code is as follows-
svg.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("class", "bar")
.append("rect")
.attr("rx", barRadius)
.attr("fill", function(d,i) {
var drilledvalue;
try {
drilledvalue = JSON.parse(parent.$("#drills").val())[colIds[0]];
} catch (e) {
}
if (typeof drilledvalue !== 'undefined' && drilledvalue.length > 0 && drilledvalue.indexOf(d[columns[0]]) !== -1) {
return drillShade;
}
if(typeof chartData[div]["transpose"] === "undefined" || chartData[div]["transpose"]=='N')
{
return getDrawColor(div, parseInt(i));//"url(#gradientBar_" + (d[columns[0]]).replace(/[^a-zA-Z0-9]/g, '', 'gi') + ")";
}
else
{
return color(0);
}
})
// .attr("color_value", "steelblue")
.attr("index_value", function(d, i) {
return "index-" + d[columns[0]].replace(/[^a-zA-Z0-9]/g, '', 'gi');
})
.attr("class", function(d, i) {
return "bars-Bubble-index-" + d[columns[0]].replace(/[^a-zA-Z0-9]/g, '', 'gi')+div;
})
.attr("id", function(d) {
return d[columns[0]] + ":" + d[measure1];
})
.attr("onclick", fun)
.attr("x", function(d) {
return x(d[columns[0]]);
})
.attr("width", x.rangeBand())
.attr("y", function(d) {
return y(d[measure1]);
})
.attr("height", function(d) {
return height - y(d[measure1]);
});
I want bar graph to appear from underneath one graph at a time. Plz help.

Without a live example , it is a bit hard to help you. But, having a look at your code, you should put the initial height at 0 and then set the final height after transition :
svg.selectAll(".bar")
//all settings
.attr("height",0)
.transition()
.duration(1000)//1 second
.attr("height",function(d)( return height - y(d[measure1]);));
EDIT:
Sorry, of course it would come from the top, you need to rotate the bars. Also, you might have to re-assess the height calculation after applying the rotation
svg.selectAll(".bar")
//all settings
.attr("height",0)
.attr("transform", "rotate(180,x,y)"); //note y must be the bottom of the chart
.transition()
.duration(1000)//1 second
.attr("height",function(d)( return height - y(d[measure1]);));

Related

d3js grouped bar chart toggling legend

I am using d3.js to render a grouped bar chart and I am looking to animate transition the bars - (show/hide different series) when clicking on the legend.
from this.
to this
perhaps also changing the scale
http://jsfiddle.net/0ht35rpb/202/
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color)
.on("click", function(d) {
console.log("d", d);
});
Some bar transition code
bars.transition()
.attr("id", function(d){ return 'tag'+d.state.replace(/\s|\(|\)|\'|\,+/g, '');})
.attr("x", function(d) { return x(d.state); })
.attr("width", x.rangeBand())
.attr("y", function(d) {return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
bars.exit().remove();
Other grouped bar chart references.
https://bl.ocks.org/mbostock/3887051
https://plnkr.co/edit/JUaLXmeCvHh0zUmrKClQ?p=preview
http://jsfiddle.net/ramseyfeng/8790t2vk/
There are a few ways to go through this. You could easily use an enter/update/exit cycle, though this is a little complex when compared to typical use of the cycle because of the nested elements and the need to set keys to ensure smooth transitions between chart states.
In this situation, it may be easier to simply use an array to hold bars that are to be filtered out, hide those bars, update the scales to not use those keys' values, and update the remaining bars.
This requires an onclick event for each legend item. When clicked, in our clicked function we manage the array of filtered out (filtered) items like so, where d is the datum associated with the legend rectangle:
// add the clicked key if not included:
if (filtered.indexOf(d) == -1) {
filtered.push(d);
// if all bars are un-checked, reset:
if(filtered.length == keys.length) filtered = [];
}
// otherwise remove it:
else {
filtered.splice(filtered.indexOf(d), 1);
}
Then we can update the scales (we need the all the keys that are not in the filtered array for the domain of the x1 scale, hence the newKeys variable):
var newKeys = [];
keys.forEach(function(d) {
if (filtered.indexOf(d) == -1 ) {
newKeys.push(d);
}
})
x1.domain(newKeys).rangeRound([0, x0.bandwidth()]);
y.domain([0, d3.max(data, function(d) { return d3.max(keys, function(key) { if (filtered.indexOf(key) == -1) return d[key]; }); })]).nice();
Then we can select our rectangles, filter by whether they should be hidden or shown, and update accordingly:
var bars = svg.selectAll(".bar").selectAll("rect")
.data(function(d) { return keys.map(function(key) { return {key: key, value: d[key]}; }); })
// filter out bars:
bars.filter(function(d) {
return filtered.indexOf(d.key) > -1;
})
.transition()
.attr("x", function(d) {
return (+d3.select(this).attr("x")) + (+d3.select(this).attr("width"))/2;
})
.attr("height",0)
.attr("width",0)
.attr("y", function(d) { return height; })
.duration(500);
// update persistent bars:
bars.filter(function(d) {
return filtered.indexOf(d.key) == -1;
})
.transition()
.attr("x", function(d) { return x1(d.key); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.attr("width", x1.bandwidth())
.attr("fill", function(d) { return z(d.key); })
.duration(500);
This solution could be made a little bit more "d3-ish" with the enter/update/exit cycle, but as our elements are relatively fixed in number, this is not as useful as in many other situations.
Here is the above code in action:
https://bl.ocks.org/andrew-reid/64a6c1892d1893009d2b99b8abee75a7
And as noted in the comments, you also need to update the axis, not just the scale. To do so, I added a class to the y scale to allow easy selection when updating the chart:
svg.select(".y")
.transition()
.call(d3.axisLeft(y).ticks(null, "s"))
.duration(500);

How to auto-sort a bar-chart with a toggle function

I've uploaded a block (FIXED) where you can toggle a sorting function.
What I want to add now is some kind of if statement when the checkbox is on, and when it is on I want the bars to sort automatically when you change year or category, and when you toggle it again it stops auto-sorting.
I thought a simple
if (document.getElementsByClassName('myCheckbox').checked) {
change();
}
Within the update function would work but nothing happens.
Any help is appreciated!
I started an answer your direct question, but soon realized that your code needed a bit of refactor. You had a bit too much copy/paste going on with redundant code and too many things drawing. When coding with d3 you should try for a single function that does all the drawing.
Here's the code running.
Here's a snippet of the new one update function to rule them all:
function update() {
file = d3.select('#year').property('value') == 'data2017' ? 'data.csv' : 'data2.csv';
catInt = d3.select('#category').property('value');
d3.csv(file, type, function(error,data) {
if(error) throw error;
var sortIndex = data.map(function(d){ return d.month});
// Update domain
y.domain([0, d3.max(data, function(d) {
return d["Category" + catInt]; })
]).nice();
// Update axis
g.selectAll(".axis.axis--y").transition()
.duration(750)
.call(yAxis);
g.selectAll(".axis.grid--y").transition()
.duration(750)
.call(yGrid);
// Sums and averages
let sumOfAll = d3.sum(data, function(d) {
return d["Category" + catInt];
});
let avgValue = d3.sum(data, function(d) {
return d["Category" + catInt];
}) / data.length;
//sort data
data.sort( d3.select("#myCheckbox").property("checked")
? function(a, b) { return b["Category" + catInt] - a["Category" + catInt]; }
: function(a, b) { return sortIndex.indexOf(a.month) - sortIndex.indexOf(b.month);})
// set x domain
x.domain(data.map(function(d) { return d.month; }));
g.selectAll(".axis.axis--x").transition()
.duration(750)
.call(xAxis);
// Update rectangles
let bars = g.selectAll(".barEnter")
.data(data, function(d){
return d.month;
});
bars = bars
.enter()
.append("rect")
.attr("class", "barEnter") // Enter data reference
.attr("width", x.bandwidth())
.merge(bars);
bars.transition()
.duration(750)
.attr("height", function(d) {
return height - y(d["Category" + catInt]);
})
.attr("x", function(d) {
return x(d.month);
})
.attr("y", function(d) {
return y(d["Category" + catInt]);
});
bars.exit().remove();
// Update text on rectangles
let textUpdate = g.selectAll(".textEnter")
.data(data, function(d){
return d.month;
});
textUpdate = textUpdate.enter()
.append("text")
.style("text-shadow","1px 1px #777")
.attr("class", "textEnter") // Enter data reference
.attr("text-anchor","middle")
.attr("font-size",11)
.attr("fill","#fff")
.merge(textUpdate);
textUpdate.transition()
.duration(750)
.attr("y", function(d) {
return y(d["Category" + catInt]) + 15;
})
// Update text value
.text( function(d) {
return d["Category" + catInt];
})
.attr("x", function(d) {
return x(d.month) + x.bandwidth()/2;
})
// Update sum and avg value
g.selectAll("#totalValue").transition()
.duration(750)
.text(sumOfAll + " Category " + catInt)
g.selectAll("#avgValue").transition()
.duration(750)
.text(formatValue(avgValue))
});
}

d3.js stacked chart animations

I'm developing a stacked chart application.
http://jsfiddle.net/NYEaX/174/
I've placed it inside a jquery plugin to create multiple instances etc... different properties and eventually different data sources.
For now I am having problems animating the chart bars and the axis.
Animate bar code
animateBars: function(selector, data){
var w = $(selector).data("width");
var h = $(selector).data("height");
var margin = methods.getMargin(h);
methods.setDimensions(w, h, margin);
//methods.setX();
//methods.setY();
//methods.setDomain(data);
var initialHeight = 0;
//var svg = d3.select(selector + " .stackedchart");
var barholder = d3.select(selector + " .barholder");
var state = barholder.selectAll(".state")
.data(data)
.enter()
.append("g")
.attr("class", "g")
.attr("x", function(d) {
return methods.x(d.Label);
})
.attr("transform", function(d) {
return "translate(" + methods.x(d.Label) + ",0)";
});
var bar = state.selectAll("rect")
.data(function(d) {
return d.blocks;
});
// Enter
bar.enter()
.append("rect")
.attr("width", methods.x.rangeBand())
.attr("y", function(d) {
return methods.y(d.y1);
})
.attr("height", function(d) {
return methods.y(d.y0) - methods.y(d.y1);
})
.style("fill", function(d) {
return methods.color(d.name);
});
// Update
bar
.attr("y", function(d) {
return methods.y(d.y1);
})
.attr("height", function(d) {
return methods.y(d.y0) - methods.y(d.y1);
})
.transition()
.duration(500)
.attr("x", function(d) {
return methods.x(d.Label);
})
.attr("width", methods.x.rangeBand())
.attr("y", function(d) {
return methods.y(d.y1);
})
.attr("height", function(d) {
return methods.y(d.y0) - methods.y(d.y1);
});
// Exit
bar.exit()
.transition()
.duration(250)
.attr("y", function(d) {
return methods.y(d.y1);
})
.attr("height", function(d) {
return methods.y(d.y0) - methods.y(d.y1);
})
.remove();
}
One problem is that "state" is generated from the "enter()" method, so all your "bar" calls are only being executed when your "g.class" is being generated, not on update. Change this:
var state = barholder.selectAll(".state")
.data(data)
.enter()
.append("g")...
to this:
var state = barholder.selectAll(".state")
.data(data);
state.enter().append("g")...
See if that helps a bit. It doesn't seem to affect your fiddle, but you might be having problems other than d3. Try simplifying your fiddle and get the d3 stuff working by itself first.

d3.js bar chart animations

I'm working on this bar chart application.
http://jsfiddle.net/NYEaX/166/
How do I
a) animate the bars so they grow from the bottom
b) morph the axis accordingly to the new data set
animateBars: function(data){
var svg = d3.select(methods.el["selector"] + " .barchart");
var barrects = d3.select(methods.el["selector"] + " .barrects");
var initialHeight = 0;
var bar = barrects.selectAll("rect")
.data(data);
// Enter
bar.enter()
.append("rect")
.attr("class", "bar")
.attr("y", initialHeight);
// Update
bar
.attr("height", initialHeight)
.transition()
.duration(500)
.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); })
// Exit
bar.exit()
.transition()
.duration(250)
.attr("y", initialHeight)
.attr("height", initialHeight)
.remove();
},
For the former, set the y attribute to be the max height instead of 0:
.attr("y", methods.height)
For the latter, recompute the x domain and call the axis component again:
methods.x.domain(data.map(function(d) { return d.letter; }));
svg.select("g.x").call(methods.xAxis);
Complete example here.

Aligning text to the left & Right of a circle depending on the data

Hi I was wondering anyone knew a suitable way of aligning text on the right or left of a circle dependent on the data it holds.
At the moment I have this & it works:
//creating the circles & text together
var node = svg.selectAll("a.node")
.data(json)
.enter().append("a")
.attr("class", "node")
;
//returning the shape & colors variable & linking it with the relevance in DB
node.append('path')
.attr("d", d3.svg.symbol().type(circle))
.attr("transform", "translate(0,0)")
;
//returning the names from the db
node.append("text")
.text(function(d) { return d.Name; })
.style("font-size", "8px")
.style("font", "Arial")
.attr('x', function (d) {
if (d.Field === "Canda"&& d.Name.length > 40) {return -180 }
else if (d.Field === "Canda") {return 9}
else {return 2}
;})
.attr('y', 2);
But, due to my json text being different lengths - when I say '-140' the texts that are shorter aren't even close to the circle. Therefore, is there a dynamic way to have the text all the same distance from the circle no matter if the length varies?
EDIT: just added .length .. but that would still mean that I would need to do more if statements as the text would be varied lengths.
http://jsfiddle.net/xwZjN/66/
A solution involving text-anchor:
force.on("tick", function() {
// edit to include text-anchor
text.attr("text-anchor", function(d) {
if( d.Country ==="USA" ) { return "end" }
else { return "start" }
} )
// edit to change x starting point
text.attr("x", function(d) {
if (d.Country ==="USA") {
return d.x - 10;
}
else {
return d.x + 6;
}
})
.attr("y", function(d) { return d.y + 4; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});

Categories

Resources