三个js 360度图像标注

问题描述 投票:0回答:1

我有一个 360 度全景图像,我使用三个 js 显示该图像。现在我想在 360 度全景图像上显示一个标记(标记:就像地图上的位置标记一样)。放大或缩小时,360 度图像应放大或缩小,但标记保持其理想的恒定位置。标记不应放大或缩小。为了更好地理解我想要的实际行为,您可以查看谷歌地图上的位置标记。 这是我如何使用三个 js 显示 360 度图像的代码。

import React, { useRef, useEffect, useState } from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
import { EVENTS, TYPES, ThreeJsDrawing } from './threeJsDrawing';
import { setThreeJsData } from './mouseEvents';
import panImg from "../assets/shot.jpg";
import panImg2 from "../assets/shot2.jpg";
import panImg3 from "../assets/shot3.png";
import panImg4 from "../assets/shot4.png";

let scene, camera, controls, sphere, polygons = [], drawingTool,
  images = [
    {
      url: panImg,
      location: {
        x: 369794,
        y: 2212918,
        lat: 20.008508,
        lng: 73.755165

      }
    },
    {
      url: panImg2,
      location: {
        x: 369779,
        y: 2212922,
        lat: 20.008382,
        lng: 73.755117
      }
    },
    {
      url: panImg3,
      location: {
        x: 369769,
        y: 2212912,
        lat: 20.008251,
        lng: 73.755074
      }
    },
    {
      url: panImg4,
      location: {
        x: 369762,
        y: 2212895,
        lat: 20.008226,
        lng: 73.755165
      }
    }
  ]
const PanoramaViewer = () => {
  const [activeImage, setActiveImage] = useState(null)
  const [renderer, setRenderer] = useState(null)

  useEffect(() => {
    if (activeImage || activeImage === 0) loadImage(activeImage)

  }, [activeImage])

  const [loading, setLoading] = useState(true)

  const containerRef = useRef(null);


  const loadImage = (index, initCall) => {
    const img = images[index]
    const [x, y] = [0, 0]
    // Load the panorama texture
    const loader = new THREE.TextureLoader();
    const texture = loader.load(img.url, (texture) => {
      // Texture loaded successfully
      // Set the texture wrapping and flipping options
      texture.wrapS = THREE.RepeatWrapping;
      // texture.repeat.x = -1;
      sphere.material.map = texture;
      sphere.material.needsUpdate = true; // Ensure material updates
      // controls.object.rotation.set(-2.3553, 1.57, 2.3553)
      // Optional: Clean up the previous texture resources
      sphere.material.map.dispose();
      setLoading(false)
    });


    if (initCall) {
      // Create a sphere geometry
      const geometry = new THREE.SphereGeometry(5, 32, 32);
      geometry.scale(-1, 1, 1);

      // Create a material with the panorama texture
      const material = new THREE.MeshBasicMaterial({ map: texture });

      // Create a mesh with the geometry and material
      sphere = new THREE.Mesh(geometry, material);
      sphere.position.set(x, y, 0);
      // Add the mesh to the scene
      scene.add(sphere);

      camera.position.set(x, y, 0.1);
      controls.target.set(x, y, 0)
      controls.update();
    }
  }

  const nextImage = () => {

    if (activeImage + 1 < images.length) {
      setLoading(true)
      setActiveImage(activeImage + 1)
    }

  }

  const prevImage = () => {

    if (activeImage - 1 >= 0) {
      setLoading(true)
      setActiveImage(activeImage - 1)
    }

  }



  const initThreeJs = () => {
    // Create a scene
    scene = new THREE.Scene();

    // Create a camera
    camera = new THREE.PerspectiveCamera(
      75,
      containerRef.current.clientWidth / containerRef.current.clientHeight,
      1,
      1000
    );

    // Create a renderer
    const renderer = new THREE.WebGLRenderer();
    setRenderer(renderer)
    renderer.setSize(containerRef.current.clientWidth, containerRef.current.clientHeight);
    containerRef.current.appendChild(renderer.domElement);

    // Add orbit controls for rotation
    controls = new OrbitControls(camera, renderer.domElement);
    controls.enableZoom = true;
    controls.rotateSpeed = -0.25; // Adjust the value as needed
    loadImage(0, true)
    setThreeJsData(renderer, scene, camera, controls)



    // Render the scene
    const animate = () => {
      requestAnimationFrame(animate);
      renderer.render(scene, camera);
    };
    animate();


    // Clean up
    return () => {
      // containerRef.current.removeEventListener('click', handleClick);
      containerRef.current.removeChild(renderer.domElement);
      controls.dispose();
    };
  }
  useEffect(initThreeJs, []);

  const Loader = () => {
    return loading ? <div style={{ position: 'absolute', width: "100%", height: "100%", display: "flex", justifyContent: "center", alignItems: "center", color: 'white' }}>Loading...</div> : <></>
  }

  const markIssue = (type) => {
    if (drawingTool) {
      console.log("if called", drawingTool)
      drawingTool.enableDrawing(type)
      drawingTool.on(EVENTS.GEOMETRY_ADDED, (geometry => {
        polygons.push(geometry)
      }))
    }
    else {
      //activate drawing tool
      console.log("else called")
      console.log("scene:", scene, "camera:", camera, "sphere:", [sphere])
      drawingTool = new ThreeJsDrawing(scene, camera, [sphere], false)
      drawingTool.enableDrawing(type)
      drawingTool.on(EVENTS.GEOMETRY_ADDED, (geometry => {
        console.log("geometry", geometry)
        polygons.push(geometry)
      }))
      console.log("polygon arrayyy:", polygons)
    }

  }

  return <>
    <Loader />
    <div style={{ position: "absolute", display: "flex", width: "100%", height: "40px", alignItems: 'center', justifyContent: 'space-around', background: "#00000082", color: 'white' }}>
      <div onClick={prevImage} style={{ cursor: "pointer" }}>prevImage</div>
      <div onClick={nextImage} style={{ cursor: "pointer" }}>nextImage</div>
    </div>
    <div style={{ position: "absolute", display: "flex", width: "100%", height: "40px", bottom: 0, alignItems: 'center', justifyContent: 'space-around', background: "#00000082", color: 'white', cursor: "pointer" }}>
      <div onClick={() => {
        markIssue("Polygon");
        console.log("polygon click")
      }}>
        polygon
      </div>
      <div onClick={() => {
        markIssue("Rectangle");
        console.log("Rectangle click")
      }}>
        Rectangle
      </div>
      <div onClick={() => {
        markIssue("Hotspot");
        console.log("Hotspot clcik")
      }}>
        Hotspot
      </div>
    </div>
    <div ref={containerRef} style={{ width: '100%', height: '100vh' }} />
  </>;
};

export default PanoramaViewer;

我尝试使用球体、css2Dobject、纹理作为标记,但它不起作用。

reactjs three.js annotations 360-degrees image-annotations
1个回答
0
投票

要使用Three.js在360度全景图像上显示标记,并确保放大或缩小时标记保持恒定位置,可以按照以下步骤操作:

  1. 使用 Three.js 几何体和材质创建标记。
  2. 设置标记相对于相机位置的位置。
  3. 当相机放大或缩小时更新标记的位置。

这是经过必要修改的代码的更新版本:

import React, { useRef, useEffect, useState } from 'react';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';

let scene, camera, controls, sphere, marker, polygons = [];

const PanoramaViewer = () => {
  // ... (previous code remains the same)

  const initThreeJs = () => {
    // ... (previous code remains the same)

    // Create a marker geometry and material
    const markerGeometry = new THREE.BoxGeometry(0.2, 0.2, 0.2);
    const markerMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
    marker = new THREE.Mesh(markerGeometry, markerMaterial);

    // Add the marker to the scene
    scene.add(marker);

    // Render the scene
    const animate = () => {
      requestAnimationFrame(animate);
      renderer.render(scene, camera);

      // Update the marker's position relative to the camera
      const markerPosition = new THREE.Vector3();
      markerPosition.set(0, 0, -5); // Adjust the position as needed
      markerPosition.applyMatrix4(camera.matrixWorld);
      marker.position.copy(markerPosition);
    };
    animate();

    // Clean up
    return () => {
      containerRef.current.removeChild(renderer.domElement);
      controls.dispose();
    };
  };

  // ... (rest of the code remains the same)

  return (
    <>
      <Loader />
      {/* ... (previous JSX code remains the same) */}
    </>
  );
};

export default PanoramaViewer;

在此代码中,我们创建一个简单的立方体标记并将其添加到场景中。我们在动画循环中更新标记相对于相机位置的位置。这可确保无论放大或缩小,标记都保持恒定位置。

您可以自定义标记的几何形状和材料以满足您的需求。确保根据需要调整标记的位置和大小。

© www.soinside.com 2019 - 2024. All rights reserved.