Skip to content

Instantly share code, notes, and snippets.

@1Cr18Ni9
Last active December 9, 2018 08:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 1Cr18Ni9/e731691d1453dea9220acbf6f35f1584 to your computer and use it in GitHub Desktop.
Save 1Cr18Ni9/e731691d1453dea9220acbf6f35f1584 to your computer and use it in GitHub Desktop.
Map - I
license: mit
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

Path animation is inspirated by Mike Bostock, the man behind the D3js.

路径动画取经于 D3js 创始人 Mike Bostock

By cliking on svg canvas, chart will switch from map to pie chart, or vice versa.

单击图形,图形会在地图和饼图中切换。

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
</style>
</head>
<body>
<div id="box"></div>
<script>
var statisticData = [
{"name":"苍南县","id":"330327", gdp: 234},
{"name":"洞头县","id":"330322", gdp: 123},
{"name":"龙湾区","id":"330303", gdp: 45},
{"name":"鹿城区","id":"330302", gdp: 200},
{"name":"平阳县","id":"330326", gdp: 156},
{"name":"瑞安市","id":"330381", gdp: 78},
{"name":"泰顺县","id":"330329", gdp: 130},
{"name":"文成县","id":"330328", gdp: 216},
{"name":"永嘉县","id":"330324", gdp: 99},
{"name":"瓯海区","id":"330304", gdp: 46},
{"name":"乐清市","id":"330382", gdp: 159}
];
d3.json("330300.json", function (err, data) {
var draw = createMap()
.geoData(data)
.statisticData(statisticData)
.width(600)
.height(500)
.colorRange(["blue", "red"]);
d3.select("#box").call(draw);
});
function createMap () {
var width = 500, height = 400, margin = 10,
colorRange = ["blue", "red"],
geoData = [], statisticData = [];
var projection, geoPath, arc, svg, holder;
function injectPieData (geoData, statisticData) {
var obj = {}, ii, color;
color = d3.scaleLinear()
.domain(d3.extent(statisticData.map(d => d.gdp)))
.range(colorRange);
for (ii = 0; ii < statisticData.length; ii++) {
obj[statisticData[ii].name] = statisticData[ii].gdp;
}
for (ii = 0; ii < geoData.features.length; ii++) {
geoData.features[ii].properties.weight =
obj[geoData.features[ii].properties.name];
}
return d3.pie()
.value(d => d.properties.weight)(geoData.features)
.map(d => {
d.color = color(d.value);
return d;
});
}
function pathTween (d1, precision) {
return function () {
var path0 = this,
path1 = path0.cloneNode(),
n0 = path0.getTotalLength(),
n1 = (path1.setAttribute("d", d1), path1).getTotalLength();
// Uniform samapling of distance based on specified precision
var distances = [0], // fist point
i = 0, dt = precision / Math.max(n0, n1);
// points between 0 and 1
while ((i += dt) < 1) { distances.push(i); }
// last point
distances.push(1);
// compute point-interpolators at each distance
var points = distances.map(function (t) {
var p0 = path0.getPointAtLength(t * n0),
p1 = path1.getPointAtLength(t * n1);
return d3.interpolate([p0.x, p0.y], [p1.x, p1.y]);
});
return function (t) {
return t < 1 ?
"M " + points.map(function (p) { return p(t); }).join("L") :
d1;
};
};
} // pathTween END
function onClick () {
var isMap = holder.node().classList.contains("map");
if (isMap) {
holder.classed("map", false);
holder.transition()
.duration(2000)
.attr("transform", "translate(" +
[width / 2, height / 2] + ")");
holder.selectAll("path")
.each(function (dd) {
d3.select(this)
.transition()
.duration(2000)
.attrTween("d", pathTween(arc(dd), 4));
});
holder.selectAll("text")
.transition()
.duration(2000)
.attr("x", d => arc.centroid(d)[0])
.attr("y", d => arc.centroid(d)[1]);
}
else {
holder.classed("map", true);
holder.transition()
.duration(2000)
.attr("transform", "translate(" + [margin, margin] + ")");
holder.selectAll("path")
.each(function (dd) {
d3.select(this)
.transition()
.duration(2000)
.attrTween("d", pathTween(geoPath(dd.data), 4));
});
holder.selectAll("text")
.transition()
.duration(2000)
.attr("x", d => geoPath.centroid(d.data.geometry)[0])
.attr("y", d => geoPath.centroid(d.data.geometry)[1]);
}
} // onClick END
function draw (selection) {
projection = d3.geoMercator()
.fitSize([width - margin * 2, height - margin * 2], geoData);
geoPath = d3.geoPath()
.projection(projection);
(function () {
var min = Math.min(width, height) - 20,
outerRadius = (min - 2 * margin) / 2;
arc = d3.arc()
.outerRadius(outerRadius)
.innerRadius(outerRadius * 0.65);
})();
var embedData = injectPieData (geoData, statisticData);
svg = selection
.append("svg")
.style("outline", "1px solid gray")
.attr("width", width)
.attr("height", height);
holder = svg.append("g")
.attr("class", "holder map")
.attr("transform",
"translate(" + [margin, margin] + ")");
holder.selectAll("path")
.data(embedData)
.enter()
.append("path")
.attr("fill", d => d.color)
.attr("stroke", "lightgray")
.attr("stroke-width", 1)
.attr("d", d => geoPath(d.data));
holder.selectAll("text")
.data(embedData)
.enter()
.append("text")
.attr("x", d => geoPath.centroid(d.data.geometry)[0])
.attr("y", d => geoPath.centroid(d.data.geometry)[1])
.text(d => d.data.properties.name)
.attr("text-anchor", "middle")
.style("text-shadow", "1px 1px 1px white");
svg.on("click", onClick);
} // function draw END
// Getter-Setter functions
draw.width = function (_) {
if (!arguments.length) { return width; }
width = _;
return draw;
};
draw.height = function (_) {
if (!arguments.length) { return height; }
height = _;
return draw;
};
draw.colorRange = function (_) {
if (!arguments.length) { return colorRange; }
colorRange = _;
return draw;
};
draw.geoData = function (_) {
if (!arguments.length) { return geoData; }
geoData = _;
return draw;
};
draw.statisticData = function (_) {
if (!arguments.length) { return statisticData; }
statisticData = _;
return draw;
};
return draw;
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment