vasturiano / react-globe.gl

React component for Globe Data Visualization using ThreeJS/WebGL
https://vasturiano.github.io/react-globe.gl/example/world-population/
MIT License
898 stars 155 forks source link

Can't auto rotate globe #42

Open ahmed-adly-khalil opened 3 years ago

ahmed-adly-khalil commented 3 years ago

Hello, I'm using react-globe-gl with next JS and it's working fine for the most part, however i can't set auto rorate for the globe to any speed. in the ThreeGlobe library i was able to achieve this by doing Globe.rotation.y += 0.005;:

   function animate() {
      tbControls.update();
      Globe.rotation.y += 0.005;
      renderer.render(scene, camera);
      requestAnimationFrame(animate);
    }

However, in next i can't see any exposed property for this, also wheh i tried to access the exposed elements like globeEl.current.controls() it's always undefined.

Here is a high level code:

import dynamic from 'next/dynamic';
const Globe = dynamic(import('react-globe.gl'), { ssr: false });
const controls = globeEl.current.controls();
export default function Home() {
  const globeEl = useRef();
  const controls = globeEl.current.controls();==> controls always undefined

also when tried

  useEffect(() => {
    globeEl.current.pointOfView({ altitude: 3.5 });
  }, []);

I got the following error

Screen Shot 2021-04-04 at 1 50 07 AM

am I missing anything?

vasturiano commented 3 years ago

@ahmed-adly-khalil are you assigning your ref to the Globe component, as in this example? https://github.com/vasturiano/react-globe.gl/blob/master/example/countries-population/index.html#L46

ahmed-adly-khalil commented 3 years ago

yes, here is my code:


import { useState, useEffect, useRef } from 'react';

import dynamic from 'next/dynamic';
const Globe = dynamic(import('react-globe.gl'), { ssr: false });

export default function Home() {
  const globeEl = useRef();
  // custom globe material

  useEffect(() => {
    console.log(globeEl.current);

  }, []);

  return (
    <div className="bg-white">

      <Globe
        ref={globeEl}
        backgroundColor={'rgba(0,0,0,0)'} width={500} height={500}
        waitForGlobeReady={true}
        globeImageUrl="/globe.png"
        showAtmosphere={true}
        atmosphereColor="blue"
        atmosphereAltitude={0.15}
        rendererConfig={{ antialias: true, alpha: true }}
      />
....
ahmed-adly-khalil commented 3 years ago

also here is my package.json

  "dependencies": {
    "next": "10.1.2",
    "react": "17.0.2",
    "react-dom": "17.0.2",
    "react-globe.gl": "^2.18.0",
    "three": "^0.127.0"
  },

and I'm getting this warning in the console, not sure if it's related Screen Shot 2021-04-04 at 7 13 12 AM

I tried to remove three from the package but it gave an error that it's required

vasturiano commented 3 years ago

@ahmed-adly-khalil I've just made this minimal sandbox with a Globe ref injection, similar to your case: https://codesandbox.io/s/globe-ref-0p05z?file=/src/App.js

If you look in the console, you'll see that the component methods are available there, including .controls(). If you do not observe the same, there might be an unrelated error on how your app setup.

The "multiple instances of Three" warning should not be an issue. It simply detects your app imported version, and the one included internally in the Globe component.

ahmed-adly-khalil commented 3 years ago

Ok, will look into it, the only thing I can think of now is next js and how it use refs

Thanks so much

TheManFromEarth1 commented 3 years ago

This is Nextjs specific and no bug.

  1. If you import it normal, you have access to the methods - but you get window undefined on first pageload.

This is because server-side rendering doesn't have access to the window-object.

  1. Nextjs does have dynamic imports like so, after which you can load the Globe fine.
const Globe = dynamic(
  () => import('react-globe.gl'),
  { ssr: false }
)

But then when you try to log the ref, the component methods are not available anymore.

On Nextjs it works, when you wrap globe in an own component, and then dynamic import that component from a higher one.

P.S. Great library btw

kjbrum commented 3 years ago

@ahmed-adly-khalil I was able to get it working with the following, instead of using Next.js dynamic import.

let Globe = () => null
if (typeof window !== 'undefined') Globe = require('react-globe.gl').default
SayedMajid commented 1 year ago

This is Nextjs specific and no bug.

  1. If you import it normal, you have access to the methods - but you get window undefined on first pageload.

This is because server-side rendering doesn't have access to the window-object.

  1. Nextjs does have dynamic imports like so, after which you can load the Globe fine.
const Globe = dynamic(
  () => import('react-globe.gl'),
  { ssr: false }
)

But then when you try to log the ref, the component methods are not available anymore.

On Nextjs it works, when you wrap globe in an own component, and then dynamic import that component from a higher one.

P.S. Great library btw

Can you please elaborate on it, I have wrapped the globe in my own components, still doesn't work..

tpatalas commented 1 year ago

I solve this issue with passing ref value as props. However, when I pass ref value with forwardRef, it does not work interestingly. Here is my version as example with next.js:

// server component
...
            <Suspense fallback={<div>Loading...</div>}>
              <WorldGlobeWithAutoRotate polygon={polygon} />
            </Suspense>
...
// client component: WorldGlobe Wrapper
'use client';

import { useRef } from 'react';
import { GlobeMethods } from 'react-globe.gl';
import { PropsWorldGlobe } from '../worldGlobe.types';
import dynamic from 'next/dynamic';

const WorldGlobe = dynamic(() => import('..').then((mod) => mod.WorldGlobe), { ssr: false });

export const WorldGlobeWithAutoRotate = ({ polygon }: PropsWorldGlobe) => {
  const globeEl = useRef<GlobeMethods>();

  return (
    <>
      <WorldGlobe
        polygon={polygon}
        globeRef={globeEl}
      />
    </>
  );
};
// client component
'use client';

import * as THREE from 'three';
import { PropsWorldGlobe } from './worldGlobe.types';
import Globe from 'react-globe.gl';
import { useEffect } from 'react';

export const WorldGlobe = ({ polygon, globeRef }: PropsWorldGlobe) => {
  const polygonsMaterial = new THREE.MeshLambertMaterial({ color: 'lightgray', side: THREE.DoubleSide });

  useEffect(() => {
    if (globeRef?.current) {
      globeRef.current.controls().autoRotate = true;
      globeRef.current.controls().autoRotateSpeed = 1.5;
      globeRef.current.pointOfView({ lat: 50, lng: -80, altitude: 2 }, 7000);
    }
  }, [globeRef]);

  return (
    <>
      <Globe
        ref={globeRef}
        animateIn={true}
        backgroundColor='rgba(0,0,0,0)'
        showGlobe={false}
        showAtmosphere={false}
        polygonsData={polygon}
        polygonCapMaterial={polygonsMaterial}
        polygonSideColor={() => 'rgba(0, 0, 0, 0)'}
        width={500}
        height={500}
      />
    </>
  );
};

One thing to note that I use server component to fetch the data instead of using useEffect. This also helps to mitigate the issue that "Window is not defined", which is caused when pre-rederngin client component in server. Hope this helps.