You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何无需重载页面更新Photo Sphere Viewer中的标记?

解决React Photo Sphere Viewer实时添加标记无需刷新的问题

你的核心问题是没有用React状态管理驱动组件更新,且修改标记数组后未通知插件实时更新。以下是具体修复方案:

关键问题分析

  1. add_pin和markers是普通变量,不是React状态,修改后不会触发组件重渲染,导致新标记无法即时显示。
  2. 依赖页面刷新让插件重新读取标记数组的方式低效,应该直接调用插件API实现实时添加。

修复步骤及完整代码

1. 用React状态替代普通变量

将模式开关和标记数组改为useState管理,同时从localStorage初始化已有数据:

const [addPinMode, setAddPinMode] = useState(false);
const [markers, setMarkers] = useState(() => {
  const saved = localStorage.getItem('markers');
  return saved ? JSON.parse(saved) : [];
});
// 用useRef保存标记ID,避免重复读取localStorage
const markerIdRef = useRef(() => {
  const id = localStorage.getItem('marker_id');
  return id ? parseInt(id) : 1;
}());

2. 保存Viewer和插件实例

在onReady中获取MarkersPlugin实例,方便后续调用添加标记的API:

const viewerRef = useRef(null);
const markersPluginRef = useRef(null);

const handleReady = (instance) => {
  viewerRef.current = instance;
  markersPluginRef.current = instance.getPlugin(MarkersPlugin);
};

3. 修改添加标记逻辑

直接调用插件API添加新标记,同步更新状态和localStorage,完全去掉页面刷新:

const handleClick = (data) => {
  if (!addPinMode) return;

  const newMarker = {
    id: markerIdRef.current.toString(),
    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>",
  };

  // 实时添加标记到全景图
  markersPluginRef.current.addMarker(newMarker);
  // 更新状态和本地存储
  const updatedMarkers = [...markers, newMarker];
  setMarkers(updatedMarkers);
  localStorage.setItem('markers', JSON.stringify(updatedMarkers));
  // 更新标记ID
  markerIdRef.current += 1;
  localStorage.setItem('marker_id', markerIdRef.current.toString());
  // 退出添加模式
  setAddPinMode(false);
};

const AddPin = () => {
  setAddPinMode(true);
};

完整修复代码

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

function App() {
  const viewerRef = useRef(null);
  const markersPluginRef = useRef(null);
  const [addPinMode, setAddPinMode] = useState(false);
  const [markers, setMarkers] = useState(() => {
    const saved = localStorage.getItem('markers');
    return saved ? JSON.parse(saved) : [];
  });
  const markerIdRef = useRef(() => {
    const id = localStorage.getItem('marker_id');
    return id ? parseInt(id) : 1;
  }());

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

  const handleClick = (data) => {
    if (!addPinMode) return;

    const newMarker = {
      id: markerIdRef.current.toString(),
      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>",
    };

    markersPluginRef.current.addMarker(newMarker);
    const updatedMarkers = [...markers, newMarker];
    setMarkers(updatedMarkers);
    localStorage.setItem('markers', JSON.stringify(updatedMarkers));
    
    markerIdRef.current += 1;
    localStorage.setItem('marker_id', markerIdRef.current.toString());
    
    setAddPinMode(false);
  };

  const AddPin = () => {
    setAddPinMode(true);
  };

  const handleReady = (instance) => {
    viewerRef.current = instance;
    markersPluginRef.current = instance.getPlugin(MarkersPlugin);
  };

  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={viewerRef}
          src={SAG} 
          height={'95vh'} 
          width="100%"
          plugins={plugins}
          onClick={handleClick}
          navbar={false}
          onReady={handleReady}
          moveInertia={false}
        />
      </div>
    </React.Fragment>
  );
}

export default App;

方案说明

  • 用useState管理状态,确保状态变化触发组件重渲染,初始标记能正确加载。
  • 直接调用MarkersPlugin的addMarkerAPI,实现标记实时显示,无需刷新页面。
  • 用useRef保存标记ID和插件实例,避免不必要的状态更新和重复读取localStorage。

内容的提问来源于stack exchange,提问作者alexolguin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 05:13:12