d3 doughpie/bubble chart combo with animation tweening - javascript

I have created this chart in static form - to exactly how it needs to be -- but I've lost animation and morphing along the way ---
1. -- how to animate the arcs
2. -- how to animate the bubbles
3. -- adding back the randomise button to test with 2 dummy data sets.
I need the arcs to tween like they did before -- http://jsfiddle.net/NYEaX/1452/
Latest fiddle.
http://jsfiddle.net/NYEaX/1506/
this is the old pie animations and worked very well
/* ------- ANIMATE PIE SLICES -------*/
var slice = doughpie.select(".slices").selectAll("path.slice")
.data(pie(data), key);
slice.enter()
.insert("path")
.style("fill", function(d) {
return color(d.data.label);
})
.style("transform", function(d, i){
//return "translate(0, 0)";
})
.attr("class", "slice");
slice
.transition().duration(1000)
.attrTween("d", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
return arc(interpolate(t));
};
})
slice.exit()
.remove();
/* ------- ANIMATE PIE SLICES -------*/
//this is the current pie arcs - but when I try and animate the pie in the same manner - it fails.
var g = svg.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d) {
return color(d.data.label);
});
arc
.outerRadius(radius - 10)
.innerRadius(0);

Related

Using General update pattern in line graph

I have a demo here
Its a line bar chart using D3 in an Angular app.
I want the chart to be responsive so when the page is resized the chart width will increase and the height will be stay the same.
I'm doing this by capturing the window resize and then calling the function that draws the chart.
This works for the axis but I cant get the line and points to redraw.
I think it's to do with the way I'm trying to us the update pattern
How can I use the update pattern to redraw this line graph
const that = this;
const valueline = d3.line()
.x(function (d, i) {
return that.x(d.date) + 0.5 * that.x.bandwidth();
})
.y(function (d) {
return that.y(d.value);
});
this.x.domain(data.map((d: any) => d.date));
this.y.domain(d3.extent(data, function (d) {
return d.value
}));
const thisLine = this.chart.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline);
const totalLength = thisLine.node().getTotalLength();
thisLine.attr("stroke-dasharray", totalLength + " " + totalLength)
.attr("stroke-dashoffset", totalLength);
thisLine.transition()
.duration(1500)
.attr("stroke-dashoffset", 0)
let circle = this.chart.selectAll("line-circle")
.data(data);
circle = circle
.enter()
.append("circle")
.attr("class", "line-circle")
.attr("r", 4)
.attr("cx", function (d) {
return that.x(d.date) + 0.5 * that.x.bandwidth();
})
.attr("cy", function (d) {
return that.y(d.value);
})
circle
.attr("r", 4)
.attr("cx", function (d) {
return that.x(d.date) + 0.5 * that.x.bandwidth();
})
.attr("cy", function (d) {
return that.y(d.value);
})
circle
.exit()
.remove()
You have problems in both circles' selection and the line selection.
The circles' selection:
You're selecting "line-circle". Instead of that, you have to select by class: ".line-circle";
You're reassigning the circle selection:
circle = circle.enter()//etc...
Don't do that, otherwise circle will point to the enter selection, not to the update selection anymore. Just do:
circle.enter()//etc...
The path:
You're appending a new path every time you call the function. Don't do that. Instead, select the existing path and change its d attribute, or append a new path if there is none. Both behaviours can be achieved with this code:
let thisLine = this.chart.selectAll(".line")
.data([data]);
thisLine = thisLine.enter()
.append("path")
.attr("class", "line")
.merge(thisLine)
.attr("d", valueline);
Here is your forked code: https://stackblitz.com/edit/basic-scatter-mt-vvdxqr?file=src/app/bar-chart.ts

update data in an d3 area graph

I'm looking for how to dynamically change data in a D3 area graph, like in this example.
The only difference, is that i'm using an area graph, like in this code:
svg.append("path")
.datum(data)
.attr("class", "d3-area")
.attr("d", area)
.style('fill', color)
.transition() // begin animation
.duration(1000)
.attrTween('d', function() {
var interpolator = d3.interpolateArray(startData, data);
return function (t) {
return area(interpolator (t));
}
});

d3 Line Chart animations from left to right

I am interested to create a line chart -- that draws itself from left to right -- essentially connecting dots in the x-axis.
http://bl.ocks.org/markmarkoh/8700606
http://bl.ocks.org/duopixel/4063326
This is the jsfiddle that I've made to start creating this chart.
http://jsfiddle.net/NYEaX/1512/
//__invoke line
$('[data-role="line"]').each(function(index) {
createLine(this);
});
function createLine(el){
var w = $(el).data("width");
var h = $(el).data("height");
var svg = d3.select($(el)[0])
.append("svg")
.attr("width", w)
.attr("height", h);
var data = d3.range(11).map(function(){return Math.random()*10})
var x = d3.scale.linear().domain([0, 10]).range([0, 700]);
var y = d3.scale.linear().domain([0, 10]).range([10, 290]);
var line = d3.svg.line()
.interpolate("cardinal")
.x(function(d,i) {return x(i);})
.y(function(d) {return y(d);})
var path = svg.append("path")
.attr("d", line(data))
.attr("stroke", "steelblue")
.attr("stroke-width", "2")
.attr("fill", "none");
var totalLength = path.node().getTotalLength();
path
.attr("stroke-dasharray", totalLength + " " + totalLength)
.attr("stroke-dashoffset", totalLength)
.transition()
.duration(2000)
.ease("linear")
.attr("stroke-dashoffset", 0);
svg.on("click", function(){
path
.transition()
.duration(2000)
.ease("linear")
.attr("stroke-dashoffset", totalLength);
});
/* plot axis */
var padding = 100; // space around the chart, not including labels
// define the x axis
var xAxis = d3.svg.axis()
.orient("bottom")
.scale(x);
//.tickFormat(date_format);
// draw x axis with labels and move to the bottom of the chart area
svg.append("g")
.attr("class", "xaxis axis") // two classes, one for css formatting, one for selection below
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);
/* plot axis */
}
One solution that you can do is to append svg circles that correspond with the path before drawing the path. Example Fiddle: http://jsfiddle.net/stancheta/ystymLm4/6/
var circles = svg.selectAll("dot")
.data(data)
.enter().append("svg:circle")
.attr('class', 'circ')
.attr("r", 3)
.attr("cx", function(d, i) { return x(i); })
.attr("cy", function(d, i) { return y(d); })
.style('fill', 'lightsteelblue');

d3.js doughnut chart :: Additional paths are rendered and not updated

I'm having problems rendering doughnut charts in d3. I have 2 doughnuts, that I'm creating side-by-side inside of a for each function::
data.forEach(function(d,i){...}
The charts render fine. When I go to update the chart, paths are redrawn. Not sure why this happening because I'm using .enter()
Any advise?
var singleDonutData = [1];
var donutSections=singleDonut.selectAll(".donutSections")
.data(singleDonutData)
.enter()
.append("g")
.attr("class","donutSections");
var pathGroup = svg.select("." + donutName).select(".donutSections").selectAll("path.arc")
.data(pie(d));
var path = pathGroup
.enter()
.append('path')
.style('fill', function(d){ //give arc a color in SVG Scale
return color(d.data.label);
})
.attr("d", arc)
.each(function(d) { this._current = d; }); // store the initial angles;
You need to add corresponding class name on the generated path, for example:
var pathGroup = svg.select("." + donutName).select(".donutSections").selectAll("path.arc")
.data(pie(d));
var path = pathGroup
.enter()
.append('path')
.style('fill', function(d){
return color(d.data.label);
})
.attr("class", "arc") // corresponding to selectAll("path.arc")
.attr("d", arc)
.each(function(d) { this._current = d; }); angles;
So that when you update the chart, d3 can correctly select these already rendered path.
After this update, you also need to add code to handle the update selection. Something like this:
pathGroup.transition().duration(1000)
.attrTween("d", function(d){
// some transition animation code here if you need it.
})

Extra values being inserted into d3 chart ordinal domain

I'm trying to create a basic d3 pie chart with a legend. I'm following the examples in two different tutorials and somehow code from one example isn't playing well with the other. What I'm trying to do is set an ordinal scale's domain so I can use that to create a legend.
On the following line, I set the domain. If I step through the code, I can see that immediately after I get ["HEURISTIC", "ADWARE", "COMPANY_BLACK_LIST", "PUP", "SUSPECTED_MALWARE", "KNOWN_MALWARE"]. This is exactly what I want.
color.domain(labels)
However, if I keep stepping through, once I reach the following line, the domain changes to ["HEURISTIC", "ADWARE", "COMPANY_BLACK_LIST", "PUP", "SUSPECTED_MALWARE", "KNOWN_MALWARE", 0, 1, 2, 3, 4, 5]
arcs.append("svg:path")
.attr("fill", function(d, i) { return color(i); } )
.attr("d", arc);
QUESTION: What is causing those six extra items to be inserted into the domain?
Code (http://jsfiddle.net/tonicboy/2urZY/5/):
var w = 150,
h = 100,
r = 50,
color = d3.scale.category20c(),
dataset = [{"name":"HEURISTIC","value":65},{"name":"ADWARE","value":75},{"name":"COMPANY_BLACK_LIST","value":9},{"name":"PUP","value":34},{"name":"SUSPECTED_MALWARE","value":14},{"name":"KNOWN_MALWARE","value":156}],
labels = _.pluck(dataset, "name");
color.domain(labels);
var chart = d3.select("#pie_chart")
.append("svg:svg")
.data([dataset])
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", "0 0 " + w + " " + h)
.attr("preserveAspectRatio", "xMinYMin meet");
var vis = chart.append("g")
.attr("transform", "translate(" + (w - r) + "," + r + ")");
var arc = d3.svg.arc()
.outerRadius(r);
var pie = d3.layout.pie()
.value(function(d) { return d.value; });
var arcs = vis.selectAll("g.slice")
.data(pie)
.enter()
.append("svg:g")
.attr("class", "slice");
arcs.append("svg:path")
.attr("fill", function(d, i) { return color(i); } )
.attr("d", arc);
var legend = chart.append("g")
.attr("class", "pie-legend")
.selectAll("g")
.data(color.domain())
.enter()
.append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 7 + ")"; });
legend.append("rect")
.attr("width", 5)
.attr("height",5)
.style("fill", color);
legend.append("text")
.attr("x", 8)
.attr("y", 9)
.text(function(d) { return d; });
Here is what the chart looks like so far:
You're setting your ordinal scale domain with strings, but then calling it with index numbers. If you ask an ordinal scale for a value that isn't currently in its domain, it will add it to the domain and assign it the next value in the range (or recycle the range values if it runs out).
Original code:
arcs.append("svg:path")
.attr("fill", function(d, i) { return color(i); } )
.attr("d", arc);
Should be
arcs.append("svg:path")
.attr("fill", function(d, i) {return color( d.data.name); } )
.attr("d", arc);
The d value is the object created by the pie chart function; it stores the original data object as d.data. The name from that data is one of the values used in the color scale domain.
Updated fiddle: http://jsfiddle.net/2urZY/6/

Categories

Resources