diff --git a/src/lib/AmapViewer/index.ts b/src/lib/AmapViewer/index.ts index 02ea074..b650837 100644 --- a/src/lib/AmapViewer/index.ts +++ b/src/lib/AmapViewer/index.ts @@ -32,7 +32,8 @@ export default class AmapViewer { } private initAmapFile() { - if ((window as any).AMap && (window as any).Loca) { + // && (window as any).Loca + if ((window as any).AMap) { this.initMap(); } else { const url = `https://webapi.amap.com/maps?v=2.0&key=${AMAP_KEY}`; @@ -53,7 +54,7 @@ export default class AmapViewer { // 获取画布dom this.amapDom = document.getElementById(this.id) as HTMLElement; this.map = new AMap.Map(this.amapDom, { - zoom: 18, + zoom: 14, center: this.center, resizeEnable: true, viewMode: "3D", diff --git a/src/pages/demo/threeAmap/index.tsx b/src/pages/demo/threeAmap/index.tsx index f6575eb..ed5d8c7 100644 --- a/src/pages/demo/threeAmap/index.tsx +++ b/src/pages/demo/threeAmap/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from "react"; +import React, { useEffect, useRef, useState } from "react"; import AmapViewer from "@/lib/AmapViewer"; import ShadowMaterialAmap from "./modules/shadow"; import styles from "./index.less"; @@ -10,15 +10,29 @@ type Props = OwnProps; const PAGE_ID = "DEMO_THREEAMAP", CENTER = [118.794694, 32.434165]; const ThreeAmap: React.FC = (props) => { + const [isAMapLoaded, setIsAMapLoaded] = useState(false); //判断是否加载完成AMap对象 const amapRef = useRef(); useEffect(() => { init(); return () => amapRef.current?.destoryMap(); }, []); + useEffect(() => { + const checkAMapLoaded = () => { + if (window.AMap && !isAMapLoaded) { + setIsAMapLoaded(true); + initLayer(); + } + }; + checkAMapLoaded(); + if (!isAMapLoaded) window.addEventListener("load", checkAMapLoaded); + return () => window.removeEventListener("load", checkAMapLoaded); + }, [isAMapLoaded]); const init = () => { amapRef.current = new AmapViewer(PAGE_ID, CENTER); + }; + const initLayer = () => { const amap = amapRef.current; - const shadowMaterialAmap = new ShadowMaterialAmap(amap); + const shadowMaterialAmap = new ShadowMaterialAmap(amapRef.current as AmapViewer); shadowMaterialAmap.CreateGLlayer(); }; return ( diff --git a/src/pages/demo/threeAmap/modules/shadow/index.ts b/src/pages/demo/threeAmap/modules/shadow/index.ts index 320245c..cd79494 100644 --- a/src/pages/demo/threeAmap/modules/shadow/index.ts +++ b/src/pages/demo/threeAmap/modules/shadow/index.ts @@ -1,24 +1,169 @@ import type AmapViewer from "src/lib/AmapViewer"; +import * as THREE from "three"; +import { Camera, MeshPhongMaterial, PerspectiveCamera, Scene, WebGLRenderer } from "three"; +const guiCtrl = { + lightPositionX: 10000, + lightPositionY: 5000, + lightPositionZ: 5000, + cameraNear: 0, + caremaFar: 20000, + cameraLeft: -5000, + cameraRight: 5000, + cameraTop: 5000, + cameraBottom: -5000, + mapSize: 512, + planeMaterialOpacity: 0.5 +}; export default class ShadowMaterialAmap { protected Amap!: any; + public locationDataSource!: any; + public meshes!: any[]; + public plane!: any; + public camera!: PerspectiveCamera; + public renderer!: WebGLRenderer; + public scene!: Scene; + public mat!: MeshPhongMaterial; + public isDestroy = false; // 是否销毁 constructor(map: AmapViewer) { this.Amap = map; + this.meshes = []; } // 创建 GL 图层 public CreateGLlayer() { - console.log("gllayer", this.Amap); - return; + this.locationDataSource = this.Amap.map.customCoords.lngLatsToCoords([ + [118.794, 32.43] // [118.795, 32.43], + // [118.796, 32.43] + ]); const gllayer = new AMap.GLCustomLayer({ zIndex: 10, // 初始化的操作 init: (gl: any) => { - console.log("gl", gl); + this.initRender(gl); + this.initScene(); + this.initCamera(); + this.initMat(); + this.addBoxGeometry(); + // const animate = () => { + // if (this.isDestroy) return; + // requestAnimationFrame(animate); + // + // // 全局的公共动画函数,添加函数可同步执行 + // this.animateEventList.forEach((event) => { + // if (event.fun && event.content) event.fun(event.content); + // }); + // }; + // animate(); + // const aLight = new THREE.AmbientLight(0xffffff, 0.3); + // this.scene.add(aLight); }, render: () => { - console.log(123); + this.renderer.resetState(); + const { near, far, fov, up, lookAt, position } = this.Amap.map.customCoords.getCameraParams(); + // 设置相机参数 + this.camera.near = near; + this.camera.far = far; + this.camera.fov = fov; + // @ts-ignore + this.camera.position.set(...position); + // @ts-ignore + this.camera.up.set(...up); + // @ts-ignore + this.camera.lookAt(...lookAt); + this.camera.updateProjectionMatrix(); + this.updateDom(); + this.renderDom(); + this.renderer.resetState(); } }); + this.Amap.map.add(gllayer); + } + + private initRender(gl: any) { + // 获取画布dom + this.renderer = new THREE.WebGLRenderer({ antialias: true, context: gl }); + } + + // 创建场景 + private initScene() { + this.scene = new THREE.Scene(); + // 环境光 + const aLight = new THREE.AmbientLight(0xffffff, 0.3); + this.scene.add(aLight); + // 平行光 + const { lightPositionX, lightPositionY, lightPositionZ, cameraNear, caremaFar, cameraLeft, cameraRight, cameraTop, cameraBottom, mapSize, planeMaterialOpacity } = guiCtrl; + const dLight = new THREE.DirectionalLight(0xff0000, 3); + dLight.position.set(lightPositionX, lightPositionY, lightPositionZ); + dLight.castShadow = true; // 开启阴影投射 + dLight.shadow.mapSize.width = mapSize; // 增加阴影分辨率 + dLight.shadow.mapSize.height = mapSize; + dLight.shadow.camera.near = cameraNear; + dLight.shadow.camera.far = caremaFar; + dLight.shadow.camera.left = cameraLeft; + dLight.shadow.camera.right = cameraRight; + dLight.shadow.camera.top = cameraTop; + dLight.shadow.camera.bottom = cameraBottom; + this.scene.add(dLight); + + // 创建接收阴影的平面 + const planeGeo = new THREE.PlaneGeometry(50000, 50000); + const shadowMat = new THREE.ShadowMaterial({ + opacity: planeMaterialOpacity + }); + + this.plane = new THREE.Mesh(planeGeo, shadowMat); + this.plane.receiveShadow = true; // 接收阴影 + this.scene.add(this.plane); + } + + // 初始化相机 + public initCamera() { + this.camera = new THREE.PerspectiveCamera(60, this.Amap.amapDom.clientWidth / this.Amap.amapDom.clientHeight, 100, 1 << 30); + } + + // 初始化背景(盒模型背景,视角在盒子里面,看到的是盒子内部) + private initMat() { + // 加载纹理 + const texture = new THREE.TextureLoader().load("https://a.amap.com/jsapi_demos/static/demo-center-v2/three.jpeg"); + texture.minFilter = THREE.LinearFilter; + // 创建材质 + this.mat = new THREE.MeshPhongMaterial({ + color: 0xfff0f0, + depthTest: true, + transparent: true, + map: texture + }); + } + + private addBoxGeometry() { + // 创建几何体 + const geo = new THREE.BoxGeometry(1000, 1000, 1000); + for (let i = 0; i < this.locationDataSource.length; i++) { + const d = this.locationDataSource[i]; + const mesh = new THREE.Mesh(geo, this.mat); + mesh.position.set(d[0], d[1], 500); + mesh.castShadow = true; // 启用阴影投射 + mesh.receiveShadow = true; // 接收阴影 + this.meshes.push({ mesh, count: i }); + this.scene.add(mesh); + } + } + + // 更新参数 + public updateDom() { + this.camera.aspect = this.Amap.amapDom.clientWidth / this.Amap.amapDom.clientHeight; // 摄像机视锥体的长宽比,通常是使用画布的宽/画布的高 + this.camera.updateProjectionMatrix(); // 更新摄像机投影矩阵。在任何参数被改变以后必须被调用,来使得这些改变生效 + // 禁用自动清理,以保持地图底图可见 + this.renderer.autoClear = false; + this.renderer.shadowMap.enabled = true; + this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; + this.renderer.setSize(this.Amap.amapDom.clientWidth, this.Amap.amapDom.clientHeight); + this.renderer.setPixelRatio(window.devicePixelRatio); // 设置设备像素比 + } + + // 渲染dom + public renderDom() { + this.renderer?.render(this.scene as Scene, this.camera as Camera); } }