Search code examples
javascriptreactjs360-panoramaphotospherephotosphereviewer

How to update markers in Photo Sphere Viewer without reloading the full page?


I am developing a 360map app, until now I have something very simple. What I want is to show an image and give the user the chance to add a marker in real time in the position of the mouse where he clicks, and then the marker should just appear on the clicked position. I can do that but I can't update the component whithout having to upload the entire website.

Here is a sample code:

import React, { useState, useEffect, useRef } from "react";
import { CompassPlugin, MarkersPlugin, ReactPhotoSphereViewer } from 'react-photo-sphere-viewer';
import PANO_TEST from './assets/img/Test_Pano.jpg';
import SAG from './assets/img/SAG.png';
import drone from './assets/img/drone.png';
import pin from './assets/img/pin-blue.png';

function App() {

  const pSRef = useRef();
  var add_pin = false
  var markers = []

  const plugins = [
    [
      MarkersPlugin,
      {
        markers: markers
      }
    ]
  ]

  const handleClick = (data) => {
    if (add_pin) {
      const chart = pSRef.current
      var id = localStorage.getItem('marker_id')
      markers.push(
        {
          // image marker that opens the panel when clicked
          id: id,
          position: { yaw: (data.data.yaw/0.01746)+"deg", pitch: (data.data.pitch/0.01752)+"deg" },
          image: pin,
          anchor: "bottom center",
          size: { width: 50, height: 50 },
          tooltip: "Mountain peak. <b>Click me!</b>",
        }
      )
      localStorage.setItem('markers',JSON.stringify(markers))
      add_pin = false
      localStorage.setItem('marker_id',parseInt(id)+1)
      
      /** HERE IS THE RELOAD THAT i WANT TO AVOID*/
      window.location.reload()
    }
  };

  const AddPin = () => {
    add_pin = true
  }

  const handleReady = (instance) => {
    console.log(instance)
  }

  return (
    <React.Fragment>

      <div style={{ height: 20, padding: "10px" }}>

        <span style={{ fontWeight: "bold", marginRight: "10px" }}>Añadir pin:</span>
        <button onClick={() => AddPin()}>AÑADIR PIN</button>

      </div>

      <div>
        <ReactPhotoSphereViewer 
          ref={pSRef}
          src={SAG} 
          height={'95vh'} 
          width={"100%"}
          plugins={plugins}
          onClick={handleClick}
          navbar={false}
          onReady={handleReady}
          moveInertia={false}
        ></ReactPhotoSphereViewer>
      </div>

    </React.Fragment>
  );
}

export default App;

Any help given will be helpfull. Thanks ;)

I have tried to use useRef to update the componente, sadly it doesn't seem to have a function that allows me to update the component instantly. I listed the functions attached to the component and none of them are usefull for this case.


Solution

  • you shouldn't add the new marker into a plain array inside of a component. there are multiple reasons some of which are, that every time the component is rendered the variable is reassigned, and when the said variable is updated it won't cause the react component to rerender. that said, I think either you should use a state like the following:

    const [markers, setMarkers] = useState([]);
    const handleClick = (data) => {
      if (add_pin) {
        setMarkers([...markers, {
              // image marker that opens the panel when clicked
              id: id,
              position: { yaw: (data.data.yaw/0.01746)+"deg", pitch: (data.data.pitch/0.01752)+"deg" },
              image: pin,
              anchor: "bottom center",
              size: { width: 50, height: 50 },
              tooltip: "Mountain peak. <b>Click me!</b>",
            }])
      }
    };
    
    

    or for another option take a look at this in the documentation you can add a new pointer like the following example:

    const handleReady = (instance) => {
      const markersPlugs = instance.getPlugin(MarkersPlugin);
      if (!markersPlugs)
        return;
      markersPlugs.addMarker({
        id: "imageLayer2",
        image: "drone.png",
        size: { width: 220, height: 220 },
        position: { yaw: '130.5deg', pitch: '-0.1deg' },
        tooltip: "Image embedded in the scene"
      });
    }
    
    return (
      <div className="App">
        <ReactPhotoSphereViewer src="Test_pano.jpg" plugins={plugins} height={'100vh'} width={"100%"} onReady={handleReady}></ReactPhotoSphereViewer>
      </div>
    );
    

    you can simply save the markersPlugs into a ref and call it as you please.