Search code examples
d3.jsreact-d3

d3.js draggable globe implementation in React


I'm new to d3.js and have litle experience on working with svgs. I can't make my globe rotate horizontally and vertically on drag. The rotation only works horizontally and the action is on the whole svg instead of just the globe. This is my code:

import * as d3 from "d3";
import { geoOrthographic, geoPath } from "d3-geo";
import { useEffect, useRef, useState } from "react";
import { feature } from "topojson-client";

export default function Globe() {
  const [geographies, setGeographies] = useState([]);
  const [width, setWidth] = useState(0);
  const [height, setHeight] = useState(0);
  const svgRef = useRef();

  useEffect(() => {
    d3.json("/data/world-data.json").then((worldData) => {
      const mapFeature = feature(worldData, worldData.objects.countries).features;
      setGeographies(mapFeature);
    });

    setWidth(window.innerWidth);
    setHeight(window.innerHeight);
  }, []);

  useEffect(() => {
    const svg = d3.select(svgRef.current);

    const projection = geoOrthographic()
      .scale(280)
      .translate([width / 2, height / 2])
      .center([0, 0])
      .rotate([0, -25]);

    const path = geoPath(projection);

    svg
      .selectAll("path")
      .data(geographies)
      .enter()
      .append("path")
      .attr("d", (d) => path(d))
      .attr("fill", "rgba(255,255,255,.5)")
      .attr("stroke", "white")
      .attr("stroke-width", 0.5);

    svg.call(
      d3.drag().on("drag", (event) => {
        const [x, y] = d3.pointer(event);
        const rotate = projection.rotate();
        const k = 5 / projection.scale();
        projection.rotate([rotate[0] + k * (x - width / 2), rotate[1]]);
        svg.selectAll("path").attr("d", (d) => path(d));
      })
    );
  }, [geographies, height, width]);

  return (
    <svg ref={svgRef} width={width} height={height} viewBox={`0 0 ${width} ${height}`}>
    </svg>
  );
}

I want to fix the interaction of the globe and also be able to rotate the globe horizontally and vertically with drag. I have tried changing the y on projection.rotate() but it isn't working as it should. Can someone help me please?

I tried changing the y value in projection.rotation(), but the interaction with the globe did not work as it should. I also tried to check examples but I didn't find nothing that could help me.


Solution

  • The error was on projection.rotate() arguments. This is my solution:

    svg.call(
      d3.drag().on("drag", (event) => {
        const rotate = projection.rotate();
        const k = 75 / projection.scale();
        projection.rotate([rotate[0] + event.dx * k, rotate[1] - event.dy * k]);
        svg.selectAll("path").attr("d", (d) => path(d));
      })
    );