Related
I built an animated choropleth map using d3 which uses your typical usa topojson file (by county). The file can be found here:
https://d3js.org/us-10m.v1.json
My code works fine, however because my data is ny based, I would like to use just a ny map (by county), as opposed to the entire united states. Like the file here for example:
https://raw.githubusercontent.com/deldersveld/topojson/master/countries/us-
states/NY-36-new-york-counties.json
However, when I replace the old file with the new one, I get the following error:
Uncaught ReferenceError: counties is not defined
I am assuming the error can be ultimately traced back to this code block:
counties = svg.append("g")
.attr("class", "counties")
.selectAll("path")
.data(topojson.feature(us, us.objects.counties).features)
.enter()
.append("path")
.attr("d", path)
.call(style,currentYear)
Specifically, this line:
.data(topojson.feature(us, us.objects.counties).features)
My assumption is because the shapefiles are slightly different, this line needs to be refactored somehow to be specific to this ny shapefile (or perhaps I'm wrong).
Anyways, here is my code. Any help would be immensely appreciated:
HTML
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/queue.v1.min.js"></script>
<svg width="960" height="600"></svg>
CSS
div.tooltip {
position: absolute;
text-align: center;
vertical-align: middle;
width: auto;
height: auto;
padding: 2px;
font: 12px sans-serif;
color: white;
background: gray;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
.counties :hover {
stroke: black;
stroke-width: 2px;
}
.county-borders {
fill: none;
stroke: #fff;
stroke-width: 0.5px;
stroke-linejoin: round;
stroke-linecap: round;
pointer-events: none;
}
.year.label {
font: 500 85px "Helvetica Neue";
fill: gray;
}
.overlay {
fill: none;
pointer-events: all;
cursor: ew-resize;
}
JS
choroplethMap();
function choroplethMap() {
var svg = d3.select("svg");
var path = d3.geoPath();
var format = d3.format("");
var height = 600;
var width = 960;
var colorScheme = d3.schemeReds[9];
colorScheme.unshift("#eee");
var color = d3.scaleQuantize()
.domain([0, 20])
.range(colorScheme);
var x = d3.scaleLinear()
.domain(d3.extent(color.domain()))
.rangeRound([600,860]);
var g = svg.append("g")
.attr("transform", "translate(0,40)");
g.selectAll("rect")
.data(color.range().map(function(d){ return color.invertExtent(d); }))
.enter()
.append("rect")
.attr("height", 8)
.attr("x", function(d){ return x(d[0]); })
.attr("width", function(d){ return x(d[1]) - x(d[0]); })
.attr("fill", function(d){ return color(d[0]); });
g.append("text")
.attr("class", "caption")
.attr("x", x.range()[0])
.attr("y", -6)
.attr("fill", "#000")
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text("Unemployment Rate (%)");
g.call(d3.axisBottom(x)
.tickSize(13)
.tickFormat(format)
.tickValues(color.range().slice(1).map(function(d){ return color.invertExtent(d)[0];
})))
.select(".domain")
.remove();
var div = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// Add the year label; the value is set on transition.
var label = svg.append("text")
.attr("class", "year label")
.attr("text-anchor", "end")
.attr("y", 575)
.attr("x", 625)
.text(2013);
queue()
// .defer(d3.json, "https://d3js.org/us-10m.v1.json")
.defer(d3.json,
"https://raw.githubusercontent.com/deldersveld/topojson/master/countries/us-
states/NY-36-new-york-counties.json")
.defer(d3.csv, "../choropleth-ny.csv")
.await(ready);
function ready(error, us, unemployment) {
if (error) throw error;
// Initialize data to 1990
var currentYear = 2013;
// Add an overlay for the year label.
var box = label.node().getBBox();
var overlay = svg.append("rect")
.attr("class", "overlay")
.attr("x", box.x)
.attr("y", box.y)
.attr("width", box.width)
.attr("height", box.height)
.on("mouseover", enableInteraction);
// Start a transition that interpolates the data based on year.
svg.transition()
.duration(25000)
.ease(d3.easeLinear)
.tween("year", tweenYear)
//.each();
counties = svg.append("g")
.attr("class", "counties")
.selectAll("path")
.data(topojson.feature(us, us.objects.counties).features)
.enter()
.append("path")
.attr("d", path)
.call(style,currentYear)
function style(counties, year){
newunemployment = interpolateData(year);
var rateById = {};
var nameById = {};
newunemployment.forEach(function(d) {
var newcode = '';
if (d.code.length < 5) {
newcode = '0' + d.code;
d.code = newcode;
}
rateById[d.code] = +d.rate;
nameById[d.code] = d.name;
});
counties.style("fill", function(d) { return color(rateById[d.id]); })
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html(nameById[d.id] + ' in ' + Math.round(currentYear) +': <br><strong>'
+ rateById[d.id] + '%</strong>')
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");})
// fade out tooltip on mouse out
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);});
}
svg.append("path")
.datum(topojson.mesh(us, us.objects.states, (a, b) => a !== b))
.attr("fill", "none")
.attr("stroke", "white")
.attr("stroke-linejoin", "round")
.attr("d", path);
// After the transition finishes, you can mouseover to change the year.
function enableInteraction() {
var yearScale = d3.scaleLinear()
.domain([2013, 2021])
.range([box.x + 10, box.x + box.width - 10])
.clamp(true);
// Cancel the current transition, if any.
svg.transition().duration(0);
overlay
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("mousemove", mousemove)
.on("touchmove", mousemove);
function mouseover() { label.classed("active", true); }
function mouseout() { label.classed("active", false); }
function mousemove() { displayYear(yearScale.invert(d3.mouse(this)[0])); }
}
// Tweens the entire chart by first tweening the year, and then the data.
// For the interpolated data, the dots and label are redrawn.
function tweenYear() {
var year = d3.interpolateNumber(2013, 2021);
return function(t) { displayYear(year(t)); };
}
// Updates the display to show the specified year.
function displayYear(year) {
currentYear = year;
counties.call(style,year)
label.text(Math.round(year));
}
// Interpolates the dataset for the given (fractional) year.
function interpolateData(year) {
return unemployment.filter(function(row) {
return row['year'] == Math.round(year);
});
}
};
};
Here is a snapshot of my csv file:
name. |. year. |. rate|. code
Bronx. 2021. 1. 36005
Bronx. 2020. 2. 36005
Queens. 2021. 4. 36081
Queens. 2017. 8. 36081
Try to replace us.objects.counties for us.objects.cb_2015_new_york_county_20m
Please, compare both images.
cbertelegni is right in noting that you need to update the property you are accessing when using the new data. Once that is resolved you have a few new problems though:
The data you have is not projected, before it was pre-projected and you didn't need a projection.
The state outline is gone as we don't have a states property in the topojson.
The first is pretty easy, we need to use a projection, perhaps something like:
var geojson = topojson.feature(topo, topo.objects.cb_2015_new_york_county_20m);
var projection = d3.geoAlbers()
.fitSize([width,height],geojson);
var path = d3.geoPath(projection);
The second problem is also fairly straightforward. The states outlines were drawn where two polygons representing two different states shared an arc: topojson.mesh(us, us.objects.states, (a, b) => a !== b) (a and b represent states, where an arc separates two different states a !== b). If we use the counties data here, we'll just get a mesh that separates the counties.
Instead we can change the equation a bit when using the counties geometry: if an arc is shared only by one feature, a and b will both represent that feature, so we can use:
var outline = topojson.mesh(topo, topo.objects.cb_2015_new_york_county_20m, (a, b) => a === b);
to find which arcs are not shared between counties (ie: the outer edges or the boundary of the state).
I've created a simplistic chorlopleth below that demonstrates the two changes in this answer in combination with cbertelegni's change.
var svg = d3.select("svg");
var path = d3.geoPath();
var format = d3.format("");
var height = 360;
var width = 500;
var names = ["Steuben","Sullivan","Tioga","Fulton","Lewis","Rockland","Schuyler","Dutchess","Westchester","Clinton","Seneca","Jefferson","Wyoming","Monroe","Chemung","Erie","Richmond","Rensselaer","Tompkins","Montgomery","Schoharie","Bronx","Franklin","Otsego","Allegany","Yates","Cortland","Ontario","Wayne","Niagara","Albany","Onondaga","Herkimer","Cattaraugus","Ulster","Nassau","Livingston","Cayuga","Chenango","Columbia","Oswego","Putnam","Greene","New York","Orange","Madison","Warren","Suffolk","Oneida","Chautauqua","Orleans","Saratoga","Schenectady","St. Lawrence","Kings","Genesee","Essex","Queens","Broome",,"Washington","Hamilton","Delaware"]
var max = 20;
var lookup = new Map();
names.forEach(function(name,i) {
lookup.set(name, max - i * max / names.length);
})
var colorScheme = d3.schemeReds[9];
colorScheme.unshift("#eee");
var color = d3.scaleQuantize()
.domain([0, 20])
.range(colorScheme);
d3.json("https://raw.githubusercontent.com/deldersveld/topojson/master/countries/us-states/NY-36-new-york-counties.json", function(topo) {
var geojson = topojson.feature(topo, topo.objects.cb_2015_new_york_county_20m);
var outline = topojson.mesh(topo, topo.objects.cb_2015_new_york_county_20m, (a, b) => a === b);
var projection = d3.geoAlbers()
.fitSize([width,height],geojson);
var path = d3.geoPath(projection);
var counties = svg.selectAll(null)
.data(geojson.features)
.enter()
.append("path")
.attr("d",path)
.attr("fill", d=> color(lookup.get(d.properties.NAME)))
var state = svg.append("path")
.attr("d", path(outline))
.attr("class","state");
})
.county-borders {
fill: none;
stroke: #fff;
stroke-width: 0.5px;
stroke-linejoin: round;
stroke-linecap: round;
pointer-events: none;
}
.state {
fill: none;
stroke: black;
stroke-dashArray: 4 6;
stroke-width: 1px;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/queue.v1.min.js"></script>
<svg width="960" height="600"></svg>
I'm attempting a choropleth map of US Counties, and am essentially using Mike Bostock's example from here https://bl.ocks.org/mbostock/4060606 I am using education instead of unemployment, but otherwise it's the same. I'd like to add just one more piece to it and have the county name display along with the rate. However, when I call the county name, I get "undefined" returned. To be clear 'rate' returns just fine, 'county' shows up undefined. Can anyone help? Thanks!
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.counties {
fill: none;
/*stroke: black;*/
}
.states {
fill: none;
stroke: #fff;
stroke-linejoin: round;
}
</style>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var Bachelor = d3.map();
var path = d3.geoPath();
var x = d3.scaleLinear()
.domain([0, 70])
.rangeRound([600, 860]);
var color_domain = [10, 20, 30, 40, 50, 60, 70]
var ext_color_domain = [10, 20, 30, 40, 50, 60, 70]
var color = d3.scaleThreshold()
.domain(color_domain)
.range([" #85c1e9", "#5dade2", "#3498db", "#2e86c1", "#2874a6", " #21618c"," #1b4f72"]);
var g = svg.append("g")
.attr("class", "key")
.attr("transform", "translate(0,40)");
g.selectAll("rect")
.data(color.range().map(function(d) {
d = color.invertExtent(d);
if (d[0] == null) d[0] = x.domain()[0];
if (d[1] == null) d[1] = x.domain()[1];
return d;
}))
.enter().append("rect")
.attr("height", 8)
.attr("x", function(d) { return x(d[0]); })
.attr("width", function(d) { return x(d[1]) - x(d[0]); })
.attr("fill", function(d) { return color(d[0]); });
g.append("text")
.attr("class", "caption")
.attr("x", x.range()[0])
.attr("y", -6)
.attr("fill", "#000")
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text("% of Adults with Bachelor's or higher");
g.call(d3.axisBottom(x)
.tickSize(13)
.tickFormat(function(x, i) { return i ? x : x; })
.tickValues(color.domain()))
.select(".domain")
.remove();
d3.queue()
.defer(d3.json, "https://d3js.org/us-10m.v1.json")
.defer(d3.tsv, "Bachelor2.tsv", function(d) { Bachelor.set(d.id, d.rate, d.county); })
.await(ready);
function ready(error, us) {
if (error) throw error;
svg.append("g")
.attr("class", "counties")
.selectAll("path")
.data(topojson.feature(us, us.objects.counties).features)
.enter().append("path")
.attr("fill", function(d) { return color(d.rate = Bachelor.get(d.id)); })
.attr("d", path)
.append("title")
.text(function(d) { return (d.county +" " d.rate +"%"); });
svg.append("path")
.datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
.attr("class", "states")
.attr("d", path);
}
</script>
There are two problems in your code.
The first problem in your code lies here:
Bachelor.set(d.id, d.rate, d.county);
In both javascript maps and D3 maps (which are slightly different), you cannot set two values for the same key. Therefore, the syntax has to be:
map.set(key, value)
Let's show it.
This works, setting the value "bar" to "foo":
var myMap = d3.map();
myMap.set("foo", "bar");
console.log(myMap.get("foo"))
<script src="https://d3js.org/d3.v4.min.js"></script>
However, this will not work:
var myMap = d3.map();
myMap.set("foo", "bar", "baz");
console.log(myMap.get("foo"))
<script src="https://d3js.org/d3.v4.min.js"></script>
As you can see, the value "baz" was not set.
That was assuming that country is a property in your Bachelor2.tsv file. And that brings us to the second problem:
In your paths, this is the data:
topojson.feature(us, us.objects.counties).features
If you look at your code, you set a property named rate (using your map) to that data...
.attr("fill", function(d) { return color(d.rate = Bachelor.get(d.id)); })
//you set 'rate' to the datum here --------^
...but you never set a property named country. Therefore, there is no such property for you to use in the callbacks.
As explained above, you cannot set it using the same map you used to set the rate. A solution is using an object instead. Alternatively, you could use two maps, which seems to be the fastest option.
I am a beginner with d3 and currently can not use the newest version, but instead I am on version 3.x.
What I am trying to realize should be simple, but sadly I didnt find resources on how to do it:
The goal is to display a path in my svg. Then I want to display e.g. a circle and transition / move / trace the circle along the path. This works fine if I want the full path to be followed.
But the goal is to follow the path only partially.
What could I do if I want the circle to start from position 0 of the path and follow it until e.g. 25% of the path?
And then again, if the circle is at 25%, how can I follow from there to 50% of the path without starting over from position 0 of the path?
I will be very thankful for any input you can provide here. Thank you very much.
I built my answer using this code from Mike Bostock (which uses D3 v3.x, as you want): https://bl.ocks.org/mbostock/1705868
First, I created a data array, specifying how much each circle should travel along the path:
var data = [0.9, 1, 0.8, 0.75, 1.2];
The values here are in percentages. So, we have 5 circles: the first one (blue in the demo below) will stop at 90% of the path, the second one (orange) at 100%, the third one (green) at 80%, the fourth one (red) at 75% and the fifth one (coloured purple in the demo) will travel 120% of the path, that is, it will travel all the length of the path and 20% more.
Then, I changed Bostock's function translateAlong to get the datum of each circle:
function translateAlong(d, path) {
var l = path.getTotalLength() * d;
return function(d, i, a) {
return function(t) {
var p = (t * l) < path.getTotalLength() ?
path.getPointAtLength(t * l) : path.getPointAtLength(t * l - path.getTotalLength());
return "translate(" + p.x + "," + p.y + ")";
};
};
}
The important piece here is:
var l = path.getTotalLength() * d;
Which will determine the final position of each circle. The ternary operator is important because of our last circle, which will travel more than 100% of the path.
Finally, we have to call the transition, passing the datum and the path itself:
circle.transition()
.duration(10000)
.attrTween("transform", function(d) {
return translateAlong(d, path.node())()
});
Here is the demo:
var points = [
[240, 100],
[290, 200],
[340, 50],
[390, 150],
[90, 150],
[140, 50],
[190, 200]
];
var svg = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 300);
var path = svg.append("path")
.data([points])
.attr("d", d3.svg.line()
.tension(0) // Catmull–Rom
.interpolate("cardinal-closed"));
var color = d3.scale.category10();
var data = [0.9, 1, 0.8, 0.75, 1.2];
svg.selectAll(".point")
.data(points)
.enter().append("circle")
.attr("r", 4)
.attr("transform", function(d) {
return "translate(" + d + ")";
});
var circle = svg.selectAll("foo")
.data(data)
.enter()
.append("circle")
.attr("r", 13)
.attr("fill", function(d, i) {
return color(i)
})
.attr("transform", "translate(" + points[0] + ")");
circle.transition()
.duration(10000)
.attrTween("transform", function(d) {
return translateAlong(d, path.node())()
});
// Returns an attrTween for translating along the specified path element.
function translateAlong(d, path) {
var l = path.getTotalLength() * d;
return function(d, i, a) {
return function(t) {
var p = (t * l) < path.getTotalLength() ?
path.getPointAtLength(t * l) : path.getPointAtLength(t * l - path.getTotalLength());
return "translate(" + p.x + "," + p.y + ")";
};
};
}
path {
fill: none;
stroke: #000;
stroke-width: 3px;
}
circle {
stroke: #fff;
stroke-width: 3px;
}
<script src="//d3js.org/d3.v3.min.js"></script>
I am using this example in D3.js as my start point and I wanted to do the same thing as in this example. With the help of stackoverflow I could already learn alot about D3 but now I'm stuck with a problem I don't understand. My code is on this repo because I don't know yet (?) how to share with you my files without wasting to much space. I get an error when I move the slider back and forth. The movement to the right will spawn points but they won't disappear when the slider is moved to the left what they are doing in the example. Also I am not sure if they always spawn the same amount points if I arrive at the end of the timeline. This image shows, when I click to end at once
and this screenshot shows when i click/slide to end of the timeline. In my unerstanding it should always spawn the same amount
<!DOCTYPE html>
<head>
<title>D3 Mapping Timeline</title>
<meta charset="utf-8">
<link rel="stylesheet" href="d3.slider.css" />
<style>
path {
fill: none;
stroke: #333;
stroke-width: .5px;
}
.land-boundary {
stroke-width: 1px;
}
.county-boundary {
stroke: #ddd;
}
.site {
opacity: 0.2;
fill: #9cf;
}
#slider3 {
margin: 20px 0 10px 20px;
width: 900px;
}
svg {
background: #eee;
}
.sphere {
fill: rgb(92, 136, 255)
}
.land {
fill: rgb(255, 239, 204)
}
.incident{
fill:#07f5e7;
opacity: 0.3;
}
.boundary {
fill: none;
stroke: rgb(224, 91, 49);
stroke-linejoin: round;
stroke-linecap: round;
vector-effect: non-scaling-stroke;
}
.state {
fill: #000;
}
.city{
fill: #de1ae8;
}
.overlay {
fill: none;
pointer-events: all;
}
</style>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="d3.slider.js"></script>
</head>
<body>
<div id="slider3"></div>
<script>
var width = 1240,
height = 720;
var projection = d3.geo.mercator()
.translate([width / 2, height / 2])
.scale((width - 1) / 2 / Math.PI);
var zoom = d3.behavior.zoom()
.scaleExtent([3, 77])
.on("zoom", zoomed);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g");
var g = svg.append("g");
var sites = svg.append("g");
svg.call(zoom)
.call(zoom.event);
d3.json("countries.topo.json", function(error, world) {
if (error) throw error;
g.append("path")
.datum({type: "Sphere"})
.attr("class", "sphere")
.attr("d", path);
g.append("path")
.datum(topojson.merge(world, world.objects.countries.geometries))
.attr("class", "land")
.attr("d", path);
g.append("path")
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
.attr("class", "boundary")
.attr("d", path);
//_______________________________________________________________________________________________________________________________________
//________________________________________________________________________________________________________________________________________
d3.json("germany.topo.json", function(error, ger){
if (error) throw error;
var states = topojson.feature(ger, ger.objects.states),
cities = topojson.feature(ger, ger.objects.cities );
g.selectAll(".states")
.data(states.features)
.enter()
.append("path")
.attr("class", "state")
.attr("class", function(d) { return "state " + d.id; })
.attr("d", path);
g.append("path")
.datum(cities)
.attr("d", path.pointRadius('0.05'))
.attr("class", "city");
});
});
function zoomed() {
g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
sites.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
d3.select(self.frameElement).style("height", height + "px");
d3.json("https://raw.githubusercontent.com/RitterLean/Slider-geojson-testing/master/vorfaelle.json", function(error, data){
console.log(data.features[1].geometry.coordinates, "sad");
window.site_data = data;
});
var displaySites = function(data) {
//console.log(data)
sites.selectAll(".site")
.data(data)
.enter()
.append("circle")
.attr("class", "site")
.attr("cx", function(d) {
var p = projection(d.geometry.coordinates);
return p[0];
})
.attr("cy", function(d) {
var p = projection(d.geometry.coordinates);
return p[1]
})
.attr("r", 0)
.transition().duration(400)
.attr("r", 0.23);
// "".attr""
sites.exit()
.transition().duration(200)
.attr("r",0)
.remove();
};
var dateParser = d3.time.format("%d.%m.%Y").parse;
var minDate = dateParser("01.01.2015");
var maxDate = dateParser("31.12.2015");
console.log(minDate);
var secondsInDay = 60 * 60 * 24;
d3.select('#slider3').call(d3.slider()
.axis(true).min(minDate).max(maxDate)
.on("slide", function(evt, value) {
newData = site_data.features.filter(function(d){
return dateParser(d.properties.date) < new Date(value);
});
console.log("New set size ", newData.length);
displaySites(newData);
})
);
</script>
</body>
Why is the slider not working properly?
The problem with the slider was:
That you are calling the exit function on selection like this:
sites.exit()
.transition().duration(200)
.attr("r",0)
.remove();
But it should have been like this:
sites.selectAll(".site")
.data(data).exit()//remove the selection which are to be removed from dataset
.transition().duration(200)
.attr("r",0)
.remove();
Working code here
Hope this helps!
I'm making a pie chart using this blocks example for growing the pie slice on mouseover. In addition to the tween, I also want to have the pie slices change colors and have a tooltip, so I want to have another function called that can work with d and i.
The problem is that although the arcTween function works as written on the mouseover, it will not work if it is wrapped in another function -- which I want to do so that I can access the index for tooltip.
Can someone explain to me why this isn't working? I have tried many ways of returning the function with different variables and can't get it working.
Full code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
stroke: #333;
fill-opacity: 90%;
stroke-width: 1.5px;
transition: fill 250ms linear;
transition-delay: 150ms;
}
path:hover {
stroke: #000;
fill-opacity: 100%;
transition-delay: 0;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var data = [44, 16, 9, 8,8,8,4,3];
function findColor(index){
var colors = [
"#cb5b49",
"#8bbbd3",
"#1f61a3",
"#c7dae4",
"#f0d0bd",
"#e89d7b",
"#a91729",
"#408cb9"];
return colors[index];
}
var width = 960,
height = 500;
var outerRadius = height / 2 - 20,
cornerRadius = 10;
var pie = d3.layout.pie();
var arc = d3.svg.arc()
.padRadius(outerRadius);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
svg.selectAll("path")
.data(pie(data))
.enter().append("path")
.each(function(d) { d.outerRadius = outerRadius - 20; })
.attr("d", arc)
.attr("fill",function(d, i){return findColor(i)})
.on("mouseover", arcTween(outerRadius + 20, 0)) //THIS WORKS
//THE BELOW DOES NOT WORK FOR SOME REASON
// .on("mouseover", function(d,i){console.log(i); return arcTween(outerRadius + 20, 0);})
.on("mouseout", arcTween(outerRadius - 20, 150));
function arcTween(outerRadius, delay) {
return function() {
d3.select(this).transition().delay(delay).attrTween("d", function(d) {
var i = d3.interpolate(d.outerRadius, outerRadius);
return function(t) { d.outerRadius = i(t); return arc(d); };
});
};
}
</script>
Per #LarsKotthoff, you need this for a fix: jsfiddle.net/vndrw57a
svg.selectAll("path")
.data(pie(data))
.enter().append("path")
.each(function(d) { d.outerRadius = outerRadius - 20; })
.attr("d", arc)
.attr("fill",function(d, i){return findColor(i)})
.on("mouseover", function(){ arcTween(outerRadius + 20, 0, this)(); })
.on("mouseout", arcTween(outerRadius - 20, 150));
function arcTween(outerRadius, delay, that) {
return function() {
var el = (that !== undefined ? that : this);
d3.select(el).transition().delay(delay).attrTween("d", function(d) {
var i = d3.interpolate(d.outerRadius, outerRadius);
return function(t) { d.outerRadius = i(t); return arc(d); };
});
};
The code
function(d,i){console.log(i); return arcTween(outerRadius + 20, 0);})
returns a function (the return value of arcTween), it doesn't execute the function. You can execute the function by adding ()
function(d,i){console.log(i); arcTween(outerRadius + 20, 0)();})