|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
|
|
.background { |
|
fill: none; |
|
pointer-events: all; |
|
} |
|
|
|
|
|
.states { |
|
fill: #aaa; |
|
stroke: #fff; |
|
stroke-width: 0.5px; |
|
stroke-linejoin: round; |
|
stroke-linecap: round; |
|
} |
|
|
|
.states.active { |
|
opacity: 1; |
|
fill: #E91D0E; |
|
stroke: #000; |
|
stroke-width: 0.5px; |
|
} |
|
|
|
</style> |
|
<body> |
|
<script src="//d3js.org/d3.v3.min.js"></script> |
|
<script src="//d3js.org/topojson.v1.min.js"></script> |
|
<script> |
|
|
|
d3.selection.prototype.moveToFront = function() { |
|
return this.each(function(){ |
|
this.parentNode.appendChild(this); |
|
}); |
|
}; |
|
|
|
var width = 960, |
|
height = 500, |
|
centered; |
|
|
|
var projection = d3.geo.albersUsa() |
|
.scale(1070) |
|
.translate([width / 2, height / 2]); |
|
|
|
var path = d3.geo.path() |
|
.projection(projection); |
|
|
|
var svg = d3.select("body").append("svg") |
|
.attr("width", width) |
|
.attr("height", height); |
|
|
|
// filters go in defs element |
|
var defs = svg.append("defs"); |
|
|
|
// create filter with id #drop-shadow |
|
// height=130% so that the shadow is not clipped |
|
var filter = defs.append("filter") |
|
.attr("id", "drop-shadow") |
|
|
|
// SourceAlpha refers to opacity of graphic that this filter will be applied to |
|
// convolve that with a Gaussian with standard deviation 3 and store result |
|
// in blur |
|
filter.append("feGaussianBlur") |
|
.attr("in", "SourceAlpha") |
|
.attr("stdDeviation", 2) |
|
.attr("result", "blur"); |
|
|
|
// translate output of Gaussian blur to the right and downwards with 2px |
|
// store result in offsetBlur |
|
filter.append("feOffset") |
|
.attr("in", "blur") |
|
.attr("dx", 0) |
|
.attr("dy", 0) |
|
.attr("result", "offsetBlur"); |
|
|
|
// overlay original SourceGraphic over translated blurred opacity by using |
|
// feMerge filter. Order of specifying inputs is important! |
|
var feMerge = filter.append("feMerge"); |
|
|
|
feMerge.append("feMergeNode") |
|
.attr("in", "offsetBlur") |
|
feMerge.append("feMergeNode") |
|
.attr("in", "SourceGraphic"); |
|
|
|
svg.append("rect") |
|
.attr("class", "background") |
|
.attr("width", width) |
|
.attr("height", height) |
|
.on("click", clicked); |
|
|
|
var g = svg.append("g"); |
|
|
|
d3.json("https://gist.githubusercontent.com/mbostock/4090846/raw/d534aba169207548a8a3d670c9c2cc719ff05c47/us.json", function(error, us) { |
|
if (error) throw error; |
|
|
|
var states = g.append("g") |
|
.selectAll("g") |
|
.data(topojson.feature(us, us.objects.states).features) |
|
.enter() |
|
.append("g") |
|
.on("click", clicked); |
|
|
|
states |
|
.append("path") |
|
.attr("d", path) |
|
.classed("states", true) |
|
}); |
|
|
|
function clicked(d) { |
|
// reset |
|
g.selectAll(".states") |
|
.classed("inactive", true) |
|
|
|
g.selectAll(".active") |
|
.classed("active", false) |
|
.style("filter", null); |
|
|
|
var x = width / 2, |
|
y = height / 2, |
|
k = 1; |
|
|
|
if (d && centered !== d) { |
|
var centroid = path.centroid(d); |
|
x = centroid[0]; |
|
y = centroid[1]; |
|
k = 2; |
|
centered = d; |
|
d3.select(this).select(".states") |
|
.classed("inactive", false); |
|
|
|
g.selectAll(".inactive") |
|
.style("opacity", 0.5) |
|
|
|
// activate |
|
d3.select(this).select(".states") |
|
.classed("active", true) |
|
.style("filter", "url(#drop-shadow)"); |
|
|
|
g.selectAll(".active") |
|
.style("opacity", 1) |
|
|
|
d3.select(this).moveToFront(); |
|
} |
|
else { |
|
// de-activate |
|
centered = null; |
|
|
|
g.selectAll(".inactive") |
|
.transition() |
|
.duration(750) |
|
.style("opacity", 1) |
|
|
|
g.selectAll(".states") |
|
.classed("inactive", false); |
|
} |
|
|
|
g |
|
.transition() |
|
.duration(750) |
|
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")") |
|
.style("stroke-width", 1.5 / k + "px"); |
|
} |
|
</script> |