Ejemplo de código
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) Crear el mapa
const initialCamera = createMapCameraPosition({
position: createGeoPoint({ latitude: 35.6812, longitude: 139.7671 }),
zoom: 12,
});
const mapViewState = useMapLibreViewState({
mapDesignType: MapLibreDesign.OsmBrightJa,
cameraPosition: initialCamera,
});
// (2) Preparar los datos y el estado de interacción
const position = useMemo(() => createGeoPoint({
latitude: 35.6812,
longitude: 139.7671,
}), []);
// (3) Renderizar el mapa y sus capas
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>
Cómo funciona el código
Superpone un lienzo transparente de Three.js sobre el mapa y ancla su objeto 3D con la proyección MapViewHolder.toScreenOffset(), independiente del proveedor.
useMapLoaded retiene el efecto hasta que el mapa existe, y luego getMapViewHolder() devuelve un holder cuyo lienzo WebGL se agrega sobre el mapa.
En cada fotograma holder.toScreenOffset(position) convierte la coordenada geográfica en un desplazamiento en píxeles, de modo que el objeto 3D permanece fijo mientras el mapa se mueve, con la misma llamada en cada proveedor.