javascript - D3.js enter() broken for svg shape animation but updates and removes them fine -
building off of malcom mclean's "d3 tips , tricks" update data dynamically , scatterplot tutorials, i'm trying take 2 different csv's of data, , through animation, plot points each when selected (using 2 html buttons).
when user hits either button, changetask function moves line, axises, , points accordingly, removes points not in new dataset.
however, new points not added (e.g. when new data should have more points current one). wrong .enter() in changetask function. if try code , data, you'll notice points on left missing when hitting reset.
full code:
<!doctype html> <meta charset="utf-8"> <style> body { font: 12px arial;} path { stroke: red; stroke-width: 2; fill: none; } .axis path, .axis line { fill: none; stroke: grey; stroke-width: 1; shape-rendering: crispedges; } .grid .tick { stroke: lightgrey; opacity: 0.7; } .grid path { stroke-width: 0; } .area { fill: lightsteelblue; stroke-width: 0; } div.tooltip { position: absolute; text-align: center; width: 60px; height: 28px; padding: 2px; font: 12px sans-serif; background: lightsteelblue; border: 0px; border-radius: 8px; pointer-events: none; } </style> <body> <div id="option"> <input name="updatebutton" type="button" value="update" onclick="changedata('update')" /> <input name="resetbutton" type="button" value="reset" onclick="changedata('reset')" /> </div> <script type="text/javascript" src="http://d3js.org/d3.v3.js"></script> <script> var margin = {top: 20, right: 20, bottom: 30, left: 50}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var parsedate = d3.time.format("%d-%b-%y").parse; var formattime = d3.time.format("%e %b"); var x = d3.time.scale().range([0, width]); var y = d3.scale.linear().range([height, 0]); // define axis var xaxis = d3.svg.axis().scale(x) .orient("bottom").ticks(5); var yaxis = d3.svg.axis().scale(y) .orient("left").ticks(5); /* // define area below line var area = d3.svg.area() .x(function(d) { return x(d.date); }) .y0(height) .y1(function(d) { return y(d.close); }); */ // define first line var valueline = d3.svg.line() .interpolate("linear") .x(function(d) { return x(d.date); }) .y(function(d) { return y(d.close); }); var div = d3.select("body").append("div") .attr("class", "tooltip") .style("opacity", 0); // create canvas var svg = d3.select("body") .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 + ")" ); // find x-axis start point function make_x_axis() { return d3.svg.axis() .scale(x) .orient("bottom") .ticks(5) } // find y-axis start point function make_y_axis() { return d3.svg.axis() .scale(y) .orient("left") .ticks(5) } original_data = "data2.csv" // , plot data d3.csv(original_data, function(error, data) { data.foreach(function(d) { d.date = parsedate(d.date); d.close = +d.close; }); // scale range of data x.domain(d3.extent(data, function(d) { return d.date; })); y.domain([0, d3.max(data, function(d) { return d.close; })]); // create line svg.append("path") .attr("class", "line") .attr("d", valueline(data)); // dot points svg.selectall("circle") .data(data) .enter() .append("circle") .attr("r", 3.5) .attr("cx", function(d) { return x(d.date); }) .attr("cy", function(d) { return y(d.close); }) .on("mouseover", function(d) { div.transition() .duration(200) .style("opacity", .9); div .html(formattime(d.date) + "<br/>" + d.close) .style("left", (d3.event.pagex) + "px") .style("top", (d3.event.pagey - 28) + "px"); }) .on("mouseout", function(d) { div.transition() .duration(500) .style("opacity", 0); }); // give graph x axis svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xaxis); svg.append("text") .attr("x", width / 2) .attr("y", height + margin.bottom) .style("text-anchor", "middle") .text("date"); // create y axis svg.append("g") .attr("class", "y axis") .call(yaxis); svg.append("text") .attr("transform", "rotate(-90)") .attr("y", 0 - margin.left) .attr("x", 0 - (height / 2)) .attr("dy", "1em") .style("text-anchor", "middle") .text("value"); // give graph title svg.append("text") .attr("x", (width / 2)) .attr("y", 0 - (margin.top / 2)) .attr("text-anchor", "middle") .style("text-decoration", "underline") .text("value vs date graph") // create vertical tick marks svg.append("g") .attr("class", "grid") .attr("transform", "translate(0," + height + ")") .call(make_x_axis() .ticksize(-height, 0, 0) .tickformat("") ) // make horizontal tick marks svg.append("g") .attr("class", "grid") .call(make_y_axis() .ticksize(-width, 0, 0) .tickformat("") ) }); // animation changes graph's data function changedata(task) { // determine dataset use depending on purpose if (task == "update") { file = "scatterplot_update.csv"; } else if (task == "reset") { file = original_data; } // data again d3.csv(file, function(error, data) { data.foreach(function(d) { d.date = parsedate(d.date); d.close = +d.close; }); // scale range of data again x.domain(d3.extent(data, function(d) { return d.date; })); y.domain([0, d3.max(data, function(d) { return d.close; })]); // select section want apply our changes var svg = d3.select("body") var circle = svg.selectall("circle").data(data) // make changes svg.select(".x.axis") // change x axis .transition() .duration(750) .call(xaxis); svg.select(".y.axis") // change y axis .transition() .duration(750) .call(yaxis); svg.select(".line") // change line .transition() .duration(750) .attr("d", valueline(data)); // update existing circles circle.transition() .duration(750) .attr("cx", function(d) { return x(d.date); }) .attr("cy", function(d) { return y(d.close); }); // enter new circles circle.enter() .append("circle") .attr("r", 10) .attr("cx", function(d) { return x(d.date); }) .attr("cy", function(d) { return y(d.close); }); // remove old circles circle.exit().remove() }); } </script> </body>
the data sets are:
data2.csv:
date,close 1-may-12,58.13 30-apr-12,53.98 27-apr-12,67 26-apr-12,89.7 25-apr-12,99 24-apr-12,130.28 23-apr-12,166.7 20-apr-12,234.98 19-apr-12,345.44 18-apr-12,443.34 17-apr-12,543.7 16-apr-12,580.13 13-apr-12,605.23 12-apr-12,622.77 11-apr-12,626.2 10-apr-12,628.44 9-apr-12,636.23 5-apr-12,633.68 4-apr-12,624.31 3-apr-12,629.32 2-apr-12,618.63 30-mar-12,599.55 29-mar-12,609.86 28-mar-12,617.62 27-mar-12,614.48 26-mar-12,606.98
scatterplot_update.csv:
date,close 10-may-12,99.55 8-may-12,0 6-may-12,67.62 4-may-12,64.48 2-may-12,60.98 1-may-12,58.13 30-apr-12,53.98 27-apr-12,67 26-apr-12,89.7 25-apr-12,99 24-apr-12,90.28 23-apr-12,106.7 20-apr-12,94.98 19-apr-12,85.44 18-apr-12,73.34 17-apr-12,53.7 16-apr-12,50.13 13-apr-12,65.23 12-apr-12,62.77 11-apr-12,66.2 10-apr-12,68.44 9-apr-12,66.23
thanks help.
because you're using data function without providing key function, d3 joining data circles corresponding index... , since second dataset smaller, there no new circles entering scene.
to fix this, add following second parameter both of data function calls:
... .data(data, function(d) {return d.date;})
which let d3 use each datum's date field join key.
Comments
Post a Comment