1
0
mirror of https://github.com/d3/d3.git synced 2025-12-08 19:46:24 +00:00
d3/docs/components/WorldMap.vue
Philippe Rivière 8e27e6530b
avoid rotation jumpiness (#3677)
* avoid rotation jumpiness

* tweak logic

---------

Co-authored-by: Mike Bostock <mbostock@gmail.com>
2023-06-13 16:33:02 +00:00

78 lines
2.3 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import * as d3 from "d3";
import * as topojson from "topojson-client";
import deferRender from "./deferRender.js";
</script>
<script>
const outline = {type: "Sphere"};
const graticule = d3.geoGraticule10();
let landPromises = {};
async function render(node, {projection, rotate, resolution, width, feature}) {
feature = await feature;
rotate = rotate && resolution === "110m" && projection.rotate();
const path = d3.geoPath(projection);
const svg = d3.select(node);
let frame;
let x0 = 0;
let x1 = 0;
function update() {
svg.selectAll("[name='outline']").attr("d", path(outline));
svg.selectAll("[name='graticule']").attr("d", path(graticule));
svg.selectAll("[name='feature']").attr("d", path(feature));
}
svg
.on("pointerenter", rotate && ((event) => {
const dx = x1 - x0;
([x1] = d3.pointer(event));
x0 = x1 - dx; // dont jump on pointerenter
}))
.on("pointermove", rotate && ((event) => {
if (!frame) frame = requestAnimationFrame(rerender);
([x1] = d3.pointer(event));
}));
function rerender() {
frame = null;
projection.rotate([rotate[0] + (x1 - x0) / width * 20, rotate[1], rotate[2]]);
update();
}
update();
}
export default {
props: {
projection: {type: Function},
land: {type: Boolean, default: true},
rotate: {type: Boolean, default: false},
resolution: {type: String, default: "110m"},
width: {type: Number, default: 688},
height: {type: Number, default: 400},
},
mounted() {
if (this.land) this.feature = landPromises[this.resolution] ??= d3
.json(`https://cdn.jsdelivr.net/npm/world-atlas@2.0.2/land-${this.resolution}.json`)
.then((world) => topojson.feature(world, world.objects.land));
this.disconnect = deferRender(this.$el, async () => render(this.$el, this));
},
updated() {
render(this.$el, this);
},
unmounted() {
if (this.disconnect) this.disconnect();
}
}
</script>
<template>
<svg :width="width" :height="height" :viewBox="[0, 0, width, height].join(' ')" style="max-width: 100%; height: auto;">
<path name="outline" fill="var(--vp-c-bg-alt)" />
<path name="graticule" stroke="currentColor" stroke-opacity="0.2" fill="none" />
<path name="feature" fill="currentColor" />
<path name="outline" stroke="currentColor" fill="none" />
</svg>
</template>