Transition to an isometric projection and back again.
See also Isometric Grid Slider.
license: gpl-3.0 |
Transition to an isometric projection and back again.
See also Isometric Grid Slider.
<!DOCTYPE html> | |
<html> | |
<head> | |
<style> | |
body { | |
margin: 0; | |
} | |
svg { | |
display: table; | |
margin: 0 auto; | |
} | |
line { | |
stroke-width: 4px; | |
} | |
path { | |
fill: none; | |
} | |
.cell { | |
stroke: #eee; | |
} | |
.outline { | |
stroke: #000; | |
stroke-width: 2px; | |
} | |
</style> | |
</head> | |
<body> | |
<script type="module"> | |
import { cross, range } from "https://cdn.skypack.dev/d3-array@3"; | |
import { geoEquirectangular, geoPath } from "https://cdn.skypack.dev/d3-geo@3"; | |
import { randomUniform } from "https://cdn.skypack.dev/d3-random@3"; | |
import { interpolateSinebow } from "https://cdn.skypack.dev/d3-scale-chromatic@3"; | |
import { select } from "https://cdn.skypack.dev/d3-selection@3"; | |
import { interval } from "https://cdn.skypack.dev/d3-timer@3"; | |
import { transition } from "https://cdn.skypack.dev/d3-transition@3"; | |
const { cos, PI, round, sin } = Math; | |
const i = 0.4; | |
const r = range(-1, 1, i); | |
const cells = { | |
type: "FeatureCollection", | |
features: cross(r, r) | |
.map(([lon, lat]) => ({ | |
type: "Feature", | |
geometry: { | |
type: "Polygon", | |
coordinates: [ | |
[ | |
[lon, lat], | |
[lon, lat + i], | |
[lon + i, lat + i], | |
[lon + i, lat], | |
[lon, lat] | |
] | |
] | |
} | |
})) | |
}; | |
const x = n => round(cos(n)); | |
const y = n => round(sin(n)); | |
const random = randomUniform(-1, 1); | |
const data = Array.from({ length: 250 }).map(d => ([random(), random()])); | |
const square = [ range(1, PI * 2.5, PI * 0.25).map(n => [x(n), y(n)]) ]; | |
const margin = {left: 1, right: 1, top: 30, bottom: 1}; | |
const size = 400; | |
const projection = geoEquirectangular() | |
.reflectY(true) | |
.fitSize([size, size], cells); | |
const path = geoPath(projection); | |
const svg = select("body").append("svg") | |
.attr("width", size + margin.left + margin.right) | |
.attr("height", size + margin.top + margin.bottom); | |
const g = svg.append("g") | |
.attr("transform", `translate(${[margin.left, margin.top]})`); | |
const cell = g.selectAll(".cell") | |
.data(cells.features) | |
.join("path") | |
.attr("class", "cell") | |
.attr("d", path); | |
const outline = g.append("path") | |
.attr("class", "outline") | |
.datum({ | |
type: "Polygon", | |
coordinates: square | |
}) | |
.attr("d", path); | |
const line = g.selectAll("line") | |
.data(data) | |
.join("line") | |
.attr("stroke", (d, i, e) => interpolateSinebow(i / e.length)) | |
.attr("y2", -margin.top + 1) | |
.attr("transform", d => `translate(${projection(d)})`); | |
let rotation = [0, 0, 0]; | |
draw(rotation); | |
interval(() => { | |
rotation = rotation[0] ? [0, 0, 0] : [30, -60, 0]; | |
draw(rotation) | |
}, 2e3); | |
function draw(rotation){ | |
projection | |
.rotate(rotation) | |
.fitSize([size, size], cells); | |
cell | |
.transition() | |
.duration(1e3) | |
.attr("d", path); | |
outline | |
.transition() | |
.duration(1e3) | |
.attr("d", path); | |
line | |
.transition() | |
.duration(1e3) | |
.attr("transform", d => `translate(${projection(d)})`); | |
} | |
</script> | |
</body> | |
</html> |