My goal is to build a Vue 3 component based on an object from D3 & Three.js created by Mike Bostock and found online.
Here is the specific object I want to incorporate: https://bl.ocks.org/mbostock/2b85250396c17a79155302f91ec21224
The HTML code provided works perfectly when copied into an html file.
I've installed d3, three, and other necessary dependencies using npm, and utilized loadScript to bring in the additional scripts. Vite is my tooling choice for this project. However, upon loading the component on a page, nothing shows up with no errors appearing in the console.
Below is the current Vue code I have. Is this approach sound? Are there better methods for integrating this object? As I am new to Vue, any guidance would be greatly appreciated.
Thank you.
<template>
<div id="globe" style="height:500px; width:700px"></div>
</template>
<script>
import * as THREE from 'three';
import * as d3 from 'd3'
import { loadScript } from "vue-plugin-load-script";
import { onMounted } from 'vue';
loadScript("https://unpkg.com/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="53273b21363613637d6b67">[email protected]</a>");
loadScript("https://unpkg.com/topojson-client@3");
loadScript("https://unpkg.com/d3-array@1");
loadScript("https://unpkg.com/d3-collection@1");
loadScript("https://unpkg.com/d3-dispatch@1");
loadScript("https://unpkg.com/d3-request@1");
loadScript("https://unpkg.com/d3-timer@1");
var globe = document.getElementById('globe');
onMounted(() => {
console.log('mounted!')
var radius = 228,
mesh,
graticule,
scene = new THREE.Scene,
camera = new THREE.PerspectiveCamera(70, globe.clientWidth/globe.clientHeight, 1, 1000),
renderer = new THREE.WebGLRenderer({alpha: true});
camera.position.z = 400;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(globe.clientWidth, globe.clientHeight);
globe.appendChild(renderer.domElement);
d3.json("https://unpkg.com/world-atlas@1/world/50m.json", function(error, topology) {
if (error) throw error;
scene.add(graticule = wireframe(graticule10(), new THREE.LineBasicMaterial({color: 0xaaaaaa})));
scene.add(mesh = wireframe(topojson.mesh(topology, topology.objects.land), new THREE.LineBasicMaterial({color: 0xff0000})));
d3.timer(function(t) {
graticule.rotation.x = mesh.rotation.x = Math.sin(t / 11000) * Math.PI / 3 - Math.PI / 2;
graticule.rotation.z = mesh.rotation.z = t / 10000;
renderer.render(scene, camera);
});
});
// Converts a point [longitude, latitude] in degrees to a THREE.Vector3.
function vertex(point) {
var lambda = point[0] * Math.PI / 180,
phi = point[1] * Math.PI / 180,
cosPhi = Math.cos(phi);
return new THREE.Vector3(
radius * cosPhi * Math.cos(lambda),
radius * cosPhi * Math.sin(lambda),
radius * Math.sin(phi)
);
}
// Converts a GeoJSON MultiLineString in spherical coordinates to a THREE.LineSegments.
function wireframe(multilinestring, material) {
var geometry = new THREE.Geometry;
multilinestring.coordinates.forEach(function(line) {
d3.pairs(line.map(vertex), function(a, b) {
geometry.vertices.push(a, b);
});
});
return new THREE.LineSegments(geometry, material);
}
// See https://github.com/d3/d3-geo/issues/95
function graticule10() {
var epsilon = 1e-6,
x1 = 180, x0 = -x1, y1 = 80, y0 = -y1, dx = 10, dy = 10,
X1 = 180, X0 = -X1, Y1 = 90, Y0 = -Y1, DX = 90, DY = 360,
x = graticuleX(y0, y1, 2.5), y = graticuleY(x0, x1, 2.5),
X = graticuleX(Y0, Y1, 2.5), Y = graticuleY(X0, X1, 2.5);
function graticuleX(y0, y1, dy) {
var y = d3.range(y0, y1 - epsilon, dy).concat(y1);
return function(x) { return y.map(function(y) { return [x, y]; }); };
}
function graticuleY(x0, x1, dx) {
var x = d3.range(x0, x1 - epsilon, dx).concat(x1);
return function(y) { return x.map(function(x) { return [x, y]; }); };
}
return {
type: "MultiLineString",
coordinates: d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X)
.concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y))
.concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) { return Math.abs(x % DX) > epsilon; }).map(x))
.concat(d3.range(Math.ceil(y0 / dy) * dy, y1 + epsilon, dy).filter(function(y) { return Math.abs(y % DY) > epsilon; }).map(y))
};
}
})
</script>