map = {
const width = 975;
const height = 610;
const context = DOM.context2d(width, height);
function draw() {
const { transform } = currentZoom;
context.clearRect(0, 0, width, height);
context.save();
context.translate(transform.x, transform.y);
context.scale(transform.k, transform.k);
context.beginPath();
context.strokeStyle = "#aaa";
context.lineWidth = 0.5 / transform.k;
for (let [id, bounds] of countyBounds) {
if (isInViewport(bounds, transform)) {
context.stroke(countyPaths.get(id));
}
}
context.beginPath();
context.strokeStyle = "#000";
context.lineWidth = 1 / transform.k;
for (let [name, bounds] of stateBounds) {
if (isInViewport(bounds, transform)) {
context.stroke(statePaths.get(name));
}
}
context.beginPath();
context.strokeStyle = "#000";
context.lineWidth = 1.5 / transform.k;
context.stroke(nationPath);
if (currentZoom.state) {
const selectedStatePath = statePaths.get(currentZoom.state);
if (selectedStatePath) {
context.fillStyle = "rgba(255, 0, 0, 0.2)";
context.fill(selectedStatePath);
}
}
context.restore();
}
function zoomToState(stateName) {
const state = topojson
.feature(us, us.objects.states)
.features.find((d) => d.properties.name === stateName);
const [[x0, y0], [x1, y1]] = path.bounds(state);
const k = Math.min(
8,
0.9 / Math.max((x1 - x0) / width, (y1 - y0) / height)
);
const centroid = [(x0 + x1) / 2, (y0 + y1) / 2];
const currentTransform = currentZoom.transform;
const start = [
(width / 2 - currentTransform.x) / currentTransform.k,
(height / 2 - currentTransform.y) / currentTransform.k,
width / currentTransform.k
];
const end = [centroid[0], centroid[1], width / k];
d3.transition()
.duration(1750)
.tween("zoom", () => (t) => {
const [x, y, s] = d3.interpolateZoom(start, end)(t);
const k = width / s;
mutable currentZoom = {
transform: d3.zoomIdentity
.translate(width / 2 - x * k, height / 2 - y * k)
.scale(k),
state: stateName
};
draw();
})
.ease(d3.easeCubic);
}
draw();
if (selectedState !== currentZoom.state) {
zoomToState(selectedState);
}
return context.canvas;
}