MapConductor React SDK Samples

อ็อบเจกต์ Three.js (เว็บเท่านั้น) | MapLibre | MapConductor React SDK

ตัวอย่าง อ็อบเจกต์ Three.js (เว็บเท่านั้น) ของ MapConductor React SDK ที่ใช้ MapLibre

ตัวอย่างเชิงโต้ตอบนี้แสดง อ็อบเจกต์ Three.js (เว็บเท่านั้น) ผ่านการห่อหุ้มของ MapConductor บน MapLibre

ตัวอย่างโค้ด

import { useEffect, useMemo } from 'react';
import * as THREE from 'three';
import { createGeoPoint, createMapCameraPosition } from '@mapconductor/js-sdk-core';
import { useMapLoaded } from '@mapconductor/js-sdk-react';
import { MapLibreDesign, MapLibreMapView2D, useMapLibreViewState } from '@mapconductor/react-for-maplibre';


// (1) สร้างแผนที่


const initialCamera = createMapCameraPosition({
  position: createGeoPoint({ latitude: 35.6812, longitude: 139.7671 }),
  zoom: 12,
});


const mapViewState = useMapLibreViewState({
  mapDesignType: MapLibreDesign.OsmBrightJa,
  cameraPosition: initialCamera,
});


// (2) เตรียมข้อมูลและสถานะการโต้ตอบ
const position = useMemo(() => createGeoPoint({
  latitude: 35.6812,
  longitude: 139.7671,
}), []);


// (3) เรนเดอร์แผนที่และโอเวอร์เลย์


function ThreeMapObject({ mapViewState, position }) {
  const isMapLoaded = useMapLoaded();


  useEffect(() => {
    if (!isMapLoaded) return;
    const holder = mapViewState.getMapViewHolder();
    if (!holder) return;


    const scene = new THREE.Scene();
    const camera = new THREE.OrthographicCamera(0, 1, 1, 0, 0.1, 1000);
    camera.position.z = 200;
    const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
    const width = holder.mapView.clientWidth;
    const height = holder.mapView.clientHeight;
    renderer.setSize(width, height);
    camera.right = width;
    camera.top = height;
    camera.updateProjectionMatrix();
    renderer.domElement.style.position = 'absolute';
    renderer.domElement.style.inset = '0';
    holder.mapView.appendChild(renderer.domElement);


    const object = new THREE.Mesh(
      new THREE.TorusKnotGeometry(13, 4),
      new THREE.MeshNormalMaterial(),
    );
    scene.add(object);


    let frame = 0;
    const draw = () => {
      const offset = holder.toScreenOffset(position);
      if (offset && !(offset instanceof Promise)) {
        object.position.set(offset.x, height - offset.y, 0);
      }
      object.rotation.y += 0.02;
      renderer.render(scene, camera);
      frame = requestAnimationFrame(draw);
    };
    draw();


    return () => {
      cancelAnimationFrame(frame);
      renderer.domElement.remove();
      renderer.dispose();
    };
  }, [isMapLoaded, mapViewState, position]);


  return null;
}


<MapLibreMapView2D state={mapViewState}>
  <ThreeMapObject mapViewState={mapViewState} position={position} />
</MapLibreMapView2D>

โค้ดนี้ทำงานอย่างไร

วางแคนวาส Three.js แบบโปร่งใสไว้เหนือแผนที่ และตรึงอ็อบเจกต์ 3D ด้วยการฉาย MapViewHolder.toScreenOffset() ที่ไม่ผูกกับผู้ให้บริการ

useMapLoaded หน่วงเอฟเฟกต์ไว้จนกว่าแผนที่จะมีอยู่จริง จากนั้น getMapViewHolder() จะคืนโฮลเดอร์ที่มีแคนวาส WebGL ต่อทับอยู่เหนือแผนที่

ในทุกเฟรม holder.toScreenOffset(position) แปลงพิกัดภูมิศาสตร์เป็นออฟเซ็ตพิกเซล อ็อบเจกต์ 3D จึงตรึงอยู่กับที่ขณะแผนที่เลื่อน โดยเรียกแบบเดียวกันบนผู้ให้บริการทุกเจ้า