Is there a way to incorporate a D3 & Three.js object into a Vue 3 component seamlessly?

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> 

Answer №1

I believe there is no need to use loadscript for d3 & THREEjs dependencies as they are already included in the npm packages you are importing.

Additionally, ensure that you install topojson-client and import it into your project.

npm i topojson-client

Here is the modified code snippet that works for me: screenshot

Some key changes I made include:

  1. When using composition api, add the <script> tag with a setup attribute: <script setup>

  2. The usage of THREE.Geometry in the wireframe function may be deprecated in newer versions of THREEjs, consider switching to THREE.BufferGeometry.

  3. d3.json() did not work for me, so instead, I used fetch to obtain the JSON data.

  4. I utilized Template Refs to access the div element, but your original approach should also function correctly.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Shorten Text - React Native

I currently have a React Native application with a FlatList component. The logic I initially implemented was to display an Expand/Collapse arrow whenever the content at the 100th position in the list is not empty. However, I now realize that this approach ...

Including item from ajax not within $.when function finished

function fetchData(){ return $.ajax({ url : 'URL1', data : { id : id }, type : 'GET', }); } function fetchItemData(item_id) { return $.ajax({ url: 'URL2', data: { item_id: it ...

Create an XML document using the DOM structure, but exclude specific elements in the process

Having some HTML that needs to be converted into an XML document, I am struggling to skip certain elements and focus only on the divs. I wrote a simple DOM traversal function to achieve this, but it seems to be stuck in an infinite loop. (More details belo ...

Passing all emitted events from Vue 3 child component to its parent - A complete guide

My Vue components are structured as follows: <TopParent> <!-- Listening for events from EventProducer here --> <Child_1> <Child_2> <Child_3> ... <Child_N> <EventProducer /> &l ...

Guide on NodeJS: Harnessing the power of nested functions to ensure synchronous execution

Imagine two functions, A and B, both interacting with a MySQL database using connection.query(...) methods. Function A utilizes a while loop to navigate through the response it receives. Subsequently, function B is invoked with the response from function ...

Issue with Sequelize Associate function not functioning correctly

I've been attempting to connect two tables in Sequelize, but I keep encountering the SequelizeEagerLoadingError indicating that one table is not associated with the other, despite trying all the suggested solutions on this platform. The tables in que ...

Add an item to one ng-repeat by clicking a button in another ng-repeat

I'm trying to manipulate two ng-repeats by adding values from one to the other and vice versa. Here's a snippet of my HTML: <div id="external-events"> <div ng-repeat="selected in selectedcolumn"> <div class="external-event b ...

Using a switch statement for camera tween in three.js

Attempting to animate a camera in three.js involves the following code in the init function, utilizing a switch statement passed through an iFrame. window.onmessage = function(evt) { switch (evt.data.cameraYpos) { case 'Ypos01&ap ...

"Exploring the Power of ZF2 with Restful APIs and Image

I am currently in the process of developing a website utilizing Zend Framework 2 in combination with AngularJS. The backend consists of a restful webservice running on ZF2, while AngularJS is used on the client side to interact with this webservice. My ne ...

How can I retrieve the ultimate value of a JQuery UI Slider and store it in a PHP variable?

I am looking to add 2 JQ UI Sliders to a single page on my PHP website. I need to capture the final values from both sliders (the last value when the user stops sliding) and store them in PHP variables for multiplication. How can I accomplish this task? I ...

Exploring the basics of Three.js: a step-by-step guide using a shadertoy example on transforming patterns with objects

Check out this snippet, inspired by the second live example found at : html, body { height: 100%; margin: 0; } #c { width: 100%; height: 100%; display: block; } <canvas id="c"></canvas> <script type="module"> // Three.j ...

What is the proper way to execute this API request and retrieve the latest news data using the Fetch method?

Note: Please note that the API Key used in this code is not a real one for privacy reasons. const url = 'http://newsapi.org/v2/everything?' + 'q=platformer&' + 'apiKey=3ce15c4d1fd3485cbcf17879bab498db'; ...

The depth map for Three.JS MeshDepthMaterial appears to have uneven distribution

I'm currently experimenting with rendering depth maps of scenes using three.js For an example, check out this link: The quality of the depth map is affected by the careful setup of the scene, as shown here: camera.position.z = 30; camera.near = 0.1 ...

Is it possible to apply a styling to a table data cell based on the table header relationship

Let's say I have a table header with content like this... <th> <div class="text-left field-sorting " rel="local_inventory"> Local inventory </div> </th> <th> <div class="text-left field-sorting " rel="something el ...

Validating the similarity of classes with JQuery

Currently, I am working on creating a quiz game using HTML, CSS, JQuery, and potentially JavaScript. I am looking to implement an if statement to check if a dropped element is placed in the correct div (city). My approach involves utilizing classes to comp ...

When dynamically loaded HTML is involved, Javascript ajax calls often fail to execute properly

Currently, I am in the process of creating a JavaScript script that can facilitate clicking through <a href> links by only replacing the inner HTML instead of reloading the entire page. The interesting thing is, it seems to be functioning properly, e ...

Troubleshooting Issue with Formidable Not Working with Multer

Attempting to develop a webpage that enables users to upload a file to a designated folder while specifying a number in an input field. Currently, I am navigating through the Multer library despite typically using body-parser. Referencing both my app.js an ...

Having trouble integrating the circular progress bar into the movie card and getting it to align properly

Struggling to position my react circular bar for movie rating in the bottom corner of the movie card. The classes are not working as expected, even though I tried to replicate it from another website using React and SCSS while I'm utilizing Material-U ...

Exploring JSON data through key-value pairs

When faced with a de-serialized JSON object that has an arbitrary structure and mixed value types... var data = { 'a':'A1', 'b':'B1', 'c': [ {'a':'A2', 'b':true}, ...

Updating item information within an array in Vue.js

I'm currently working on integrating an edit feature into a task application using Vue JS. My issue lies in the fact that I have a click event assigned to the edit button - @click="editShow" which displays input fields for editing all items instead ...