whytheq whytheq - 6 days ago 9
Javascript Question

Ensure transition completes

I have the following pie chart with a very nice transition:

http://plnkr.co/edit/b4uLimUSZzxiPzAkNpBt?p=preview

The code for the pie chart is the following:

function addPieChart(meas, dataFile) {

var width = 400,
height = 400,
radius = Math.min(width, height) / 2.1,
color = d3.scale.ordinal()
.range(["#016da9", "#4c96d5"])
.domain([0, 1]);


//check if the svg already exists
var plot = d3.select("#svgPIEChart")
if (plot.empty()) {
var vis = d3.select("#pieChart")
.append("svg") //create the SVG element
.attr({
id: "svgPIEChart"
})
} else {
var vis = d3.select("#svgPIEChart")
vis.selectAll("*").remove();
};

//svg element
vis.attr({
//set the width and height of our visualization (these will be attributes of the <svg> tag
width: width,
height: height
});

//group of the svg element
var svg = vis
.append("g")
.attr({
'transform': "translate(" + width / 2 + "," + height * .52 + ")"
});


var arc = d3.svg.arc()
.startAngle(function(d) {
return d.x;
})
.endAngle(function(d) {
return d.x + d.dx;
})
.outerRadius(function(d) {
return (d.y + d.dy) / (radius);
})
.innerRadius(function(d) {
return d.y / (radius);
});


d3.text(dataFile, function(text) {
var csv = d3.csv.parseRows(text);
var json = buildHierarchy(csv);



// it seems d3.layout.partition() can be either squares or arcs
var partition = d3.layout.partition()
.sort(null)
.size([2 * Math.PI, radius * radius])
.value(function(d) {
return d.Revenue;
});

var path = svg.data([json]).selectAll(".theArc")
.data(partition.nodes)
.enter()
.append("path")
.attr("class", "theArc") //<<<<<<<<<<new jq
.attr("id", function(d, i) {
return "theArc_" + i;
}) //Give each slice a unique ID //<<<<<<<<<<new jq
.attr("display", function(d) {
return d.depth ? null : "none";
})
.attr("d", arc)
.style("stroke", "#fff")
.style("fill", function(d) {
return color((d.children ? d : d.parent).name);
})
.attr("fill-rule", "evenodd")
.style("opacity", 1)
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("click", up)
.each(stash);

// path.each(stash).on("click", up)

//this is a start>>>>>
path
.append("title") //mouseover title showing the figures
.text(function(d) {
return d.name + ": " + formatAsShortInteger(d.Revenue);
});

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
svg.data([json]).selectAll(".theTxts")
.data(partition.nodes)
.enter()
.append("text")
.attr("class", "theTxts")
.attr("dx", 10) //Move the text from the start angle of the arc
.attr("dy", 15) //Move the text down
.append("textPath")
.attr("class", "foo")
.attr("xlink:href", function(d, i) {
return "#theArc_" + i;
})
.text(function(d) {
if ((d.name != 'root') && ((d.name != 'B T') || (currentMeasure != 'W'))) {
return d.name;
}
})
.on("click", up) //<<<new jq;

svg.append("text")
.attr({
x: "0",
y: "0",
'class': "title",
"id": "titleX",
'text-anchor': "middle"
})
.text(currentMeasure + " split")
//>>
.append("tspan")
.attr({
dy: "1.1em",
x: 0,
'text-anchor': "middle"
})
.text("for " + latestMth)
.attr("class", "title");



d3.selectAll("input").on("change", function change() {

value = createValueFunc(this.value);
currentMeasure = this.value;

var path2 = svg.data([json]).selectAll(".theArc");
path2
.data(partition.value(value).nodes)
.transition()
.duration(1500)
.attrTween("d", arcTween)


//to update the tooltips
svg.selectAll("title")
.text(function(d) {
return d.name + ": " + formatAsShortInteger(value(d));
});


svg.selectAll("textPath")
.text(function(d) {
if ((d.name != 'root') && ((d.name != 'B T') || (currentMeasure != 'W'))) {
return d.name;
}
});

// the following deletes what was originally created and then recreates the text
svg.selectAll("#titleX").remove();
svg.append("text")
.attr({
x: "0",
y: "0",
'class': "title",
"id": "titleX",
'text-anchor': "middle"
})
.text(currentMeasure + " split")
.append("tspan")
.attr({
dy: "1.1em",
x: 0,
'text-anchor': "middle"
})
.text("for " + latestMth)
.attr("class", "title");


addProdTitl();
updateTOPbarChart(currentGrp, currentMeasure, currentColr);
updateBOTbarChart(currentGrp, currentMeasure, currentColr);

});



function mouseover() {
d3.select(this)
.transition()
.duration(200)
.style("opacity", 0.9);
};

function mouseout() {
d3.select(this)
.transition()
.duration(200)
.style("opacity", 1);
};


// update bar chart when user selects piece of the pie chart
function up(d, i) {

currentGrp = d.name;

// (d.children ? d : d.parent).name
currentColr = color((d.children ? d : d.parent).name); // color(d.name);

addProdTitl();
updateTOPbarChart(d.name, currentMeasure, currentColr); //color(d.name));
updateBOTbarChart(d.name, currentMeasure, currentColr); //color(d.name));

};


// Stash the old values for transition.
function stash(d) {
d.x0 = d.x;
d.dx0 = d.dx;
};

// Interpolate the arcs in data space.
function arcTween(a) {
var i = d3.interpolate({
x: a.x0,
dx: a.dx0
}, a);
return function(t) {
var b = i(t);
a.x0 = b.x;
a.dx0 = b.dx;
return arc(b);
};
};


});


};


If you change the selection of the radio button and then click on a section of the pie chart that section does not complete its transition as I believe it decides it needs to move on to its "on" event.

How do I ensure that the transition completes? (and the pie chart does not do a pac-man impersonation)

Answer
  1. Clear your listeners when the tranistion starts.
  2. Attach it when the transition is ended

Like this:

 path2
                    .data(partition.value(value).nodes)
                    .transition()
                    .duration(1500)
                    .attrTween("d", arcTween)
                    .each("start", function(){
                      d3.select(this)
                      .on("mouseover", null) //CLEARING the listeners
                      .on("mouseout", null) //CLEARING the listeners
                      .on("click", null) //CLEARING the listeners
                    })
                    .each("end", function(){
                      d3.select(this)
                      .on("mouseover", mouseover) //attaching the listeners
                      .on("mouseout", mouseout) //attaching the listeners
                      .on("click", up) ////attaching the listeners
                    });

working code here

Comments