This is similar to Map Pan & Zoom IV but with unprojected raw GeoJSON, which is scaled to fit the canvas.
xxxxxxxxxx
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
var width = 960,
height = 500
var zoom = d3.zoom()
.scaleExtent([1, 100])
.on('zoom', zoomed)
var canvas = d3.select("body").append("canvas")
.attr("width", width)
.attr("height", height)
var context = canvas.node().getContext("2d")
context.lineJoin = "round"
context.lineCap = "round"
context.lineWidth = 0.5
var mercator = d3.geoMercator()
var transform = d3.geoIdentity()
var path = d3.geoPath()
.projection({stream: function(s) { return mercator.stream(transform.stream(s)) }})
.context(context)
var render = function () {}
d3.json('ecuador-simplified.geojson', onLoad)
function onLoad (err, geojson) {
if (err) throw err
mercator
.scale(1)
.translate([0, 0])
var b = path.bounds(geojson),
dx = b[1][0] - b[0][0],
dy = b[1][1] - b[0][1],
x = (b[0][0] + b[1][0]) / 2,
y = (b[0][1] + b[1][1]) / 2,
s = .95 / Math.max(dx / width, dy / height),
t = [width / 2 - s * x, height / 2 - s * y]
mercator
.scale(s)
.translate(t)
render = function() {
context.fillStyle = "#fff", context.fillRect(0, 0, width, height);
context.beginPath(), path(geojson), context.strokeStyle = "#000", context.stroke()
}
canvas.call(zoom)
render()
}
function zoomed () {
var t = d3.event.transform
transform
.translate([t.x, t.y])
.scale(t.k)
render()
}
</script>
</body>
https://d3js.org/d3.v4.min.js