当前位置: 首页 > backend >正文

高德地图API + three.js + Vue3基础使用与使用 + 标记不显示避坑

three.js小白的学习之路。

最近闲来无事,突然想起来之前好像项目有需求说是要将模型放在地图上。加上在浏览别的大佬写的博客时,也找到了一些大佬写的相关文章。基本上都是使用的高德地图开放平台的JS API。我也随之开启了自己的学习之路。

先简单学习了一些在高德地图上加入点标记、图层叠加、样式修改的API,不得不佩服人家确实厉害,操作简单,灵活度高,而且API从1.x升级到2.0之后,对性能优化的非常明显。

但是单纯的学习过程有些枯燥,就简单浏览了一遍,等用到的时候再来翻一遍。

然后目标直指自定义图层中的与three.js相结合的API的使用。

基础搭建

首先我使用的是Vite+Vue3+TS的框架,于是我安装了这几个插件:

yarn add @amap/amap-jsapi-loader
yarn add @amap/amap-jsapi-types

前一个是适用于Vue的插件形式,就和我们平常使用第三方包一样。但是这个第三方包没有提供任何有关type的信息,导致编程过程中没有提示。于是就安装了第二个插件,但是啊但是,这个插件包含的也只有一部分类型信息,很多与three.js相关的类型信息是没有的,难受。

接下来新建一个vue文件,构建基础框架:

<template><div id="container"></div>
</template><script lang="ts" setup>
import AMapLoader from "@amap/amap-jsapi-loader";
import "@amap/amap-jsapi-types";onMounted(() => {window._AMapSecurityConfig = {securityJsCode: "安全密钥",};AMapLoader.load({key: "申请的key值", // 申请好的Web端开发者Key,首次调用 load 时必填version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15}).then(() => {map = new AMap.Map("container", {center: [116.54, 39.79],zooms: [2, 20],zoom: 14,viewMode: "3D",pitch: 50, // 俯仰角度,默认0,范围0-83,0相当于正交视图});}).catch((e) => {console.log(e);});});
</script><style scoped>
#container {width: 100vw;height: 100vh;
}
</style>

key和安全密钥需要自己去官网申请,跟着官网步骤走即可。

container是容器,和three.js类似。

AMapLoader是核心,在then的回调中,假如你是用的是js,没有引用@amap/amap-jsapi-types,那么then的回调函数里面将返回一个AMap参数供你使用。

AMapLoader.load().then((AMap) => {// …………
})

其实打开插件也可以看到,AMap是一个命名空间,将所有的API都放在了这个命名空间里面。

这样,打开网页就可以看到如下的画面了:

与three.js结合

高德地图官方提供了专门与three.js结合使用的API,是一个单独的自定义层级结构,GLCustomLayer类。

1.构造函数

const glLayer = new AMap.GLCustomLayer(opts: GlCustomLayerOptions)

2.参数opts

属性描述
opts.init 类型:Function初始化的时候,开发者可以在这个函数参数里面获取 gl 上下文,进行一些初始化的操作。
opts.render 类型:Function绘制函数,初始化完成时候,开发者需要给该图层设定render方法, 该方法需要实现图层的绘制,API会在合适的时机自动调用该方法
opts.zooms 类型:[Number, Number] default [2,20]图层缩放等级范围,默认 [2, 20]
opts.opacity 类型:Number default 1图层透明度,默认为 1
opts.visible 类型:Boolean default true图层是否可见,默认为 true
opts.zIndex 类型:Number default 120图层的层级,默认为 120

3.成员函数

getMap():返回GLCustomLayer所属地图的实例;返回值:Map| null

getzIndex():获取GLCustomLayer叠加顺序;返回值:number

setzIndex(index):设置GLCustomLayer叠加顺序;参数:number,叠加值;返回值:void

getOpacity():获取GLCustomLayer透明度;返回值:number

setOpacity(opacity):设置GLCustomLayer透明度;参数:number,透明度;返回值:void

getZooms():获取GLCustomLayer显示层级范围;返回值:number

setZooms(zooms):设置GLCustomLayer显示层级范围;参数:Vector,默认[3, 20];返回值:number

show():显示GLCustomLayer;返回值:void

hide():隐藏GLCustomLayer;返回值:void

示例代码

<template><div id="container"></div>
</template><script lang="ts" setup>
import * as Three from "three";
import AMapLoader from "@amap/amap-jsapi-loader";
import "@amap/amap-jsapi-types";let map: AMap.Map;
let camera: Three.PerspectiveCamera,scene: Three.Scene,renderer: Three.WebGLRenderer;
const meshArr: Three.Mesh[] = [];let customCoords;
let data;const init = (gl: any) => {// 这里我们的地图模式是 3D,所以创建一个透视相机,相机的参数初始化可以随意设置,因为在 render 函数中,每一帧都需要同步相机参数,因此这里变得不那么重要。// 如果你需要 2D 地图(viewMode: '2D'),那么你需要创建一个正交相机camera = new Three.PerspectiveCamera(60,window.innerWidth / window.innerHeight,10,1000000);renderer = new Three.WebGLRenderer({context: gl, // 传递canvas的gl上下文});// 自动清空画布这里必须设置为 false,否则地图底图将无法显示renderer.autoClear = false;scene = new Three.Scene();{// 环境光照和平行光const aLight = new Three.AmbientLight(0xffffff, 0.3);const dLight = new Three.DirectionalLight(0xffffff, 1);dLight.position.set(1000, -100, 900);scene.add(dLight);scene.add(aLight);}const texture = new Three.TextureLoader().load("https://a.amap.com/jsapi_demos/static/demo-center-v2/three.jpeg");texture.minFilter = Three.LinearFilter;const mat = new Three.MeshPhongMaterial({color: 0xfff0f0,depthTest: true,transparent: true,map: texture,});const geo = new Three.BoxGeometry(500, 500, 500);for (let i = 0; i < data.length; i++) {const pos = data[i];const mesh = new Three.Mesh(geo, mat);mesh.position.set(pos[0], pos[1], 500);meshArr.push(mesh);scene.add(mesh);}
};const render = () => {// 这里必须执行!!重新设置 three 的 gl 上下文状态。renderer.resetState();// 重新设置图层的渲染中心点,将模型等物体的渲染中心点重置, 否则和 LOCA 可视化等多个图层能力使用的时候会出现物体位置偏移的问题customCoords.setCenter([116.52, 39.79]);const { near, far, fov, up, lookAt, position } =customCoords.getCameraParams();// 这里的顺序不能颠倒,否则可能会出现绘制卡顿的效果。camera.near = near;camera.far = far;camera.fov = fov;camera.position.set(...position);camera.up.set(...up);camera.lookAt(...lookAt);camera.updateProjectionMatrix();renderer.render(scene, camera);// 这里必须执行!!重新设置 three 的 gl 上下文状态。renderer.resetState();
};onMounted(() => {window._AMapSecurityConfig = {securityJsCode: "你的安全密钥",};AMapLoader.load({key: "你的key", // 申请好的Web端开发者Key,首次调用 load 时必填version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15}).then(() => {map = new AMap.Map("container", {center: [116.54, 39.79],zooms: [2, 20],zoom: 14,viewMode: "3D",pitch: 50, // 俯仰角度,默认0,范围0-83,0相当于正交视图});// 数据转换工具customCoords = map.customCoords;// 数据使用转换工具进行转换,这个操作必须要提前执行(在获取镜头参数 函数之前执行),否则将会获得一个错误信息。data = customCoords.lngLatsToCoords([[116.52, 39.79],[116.54, 39.79],[116.56, 39.79],]);// 创建GL图层const glLayer = new AMap.GLCustomLayer({zIndex: 10,init,render,});map.add(glLayer);}).catch((e) => {console.log(e);});
});
</script><style scoped>
#container {width: 100vw;height: 100vh;
}
</style>

运行结果:

三个Box已经跃然纸上。

AMap.Map.customCoords

这里在多一嘴,说一下用到的一个方法,AMap.Map.customCoords

我在jsap-types的插件里面是没有找到这个的。其实这个是高德地图 JS API 2.0 中的一个工具,用于在 3D 地图场景下进行坐标转换。它主要用于将经纬度坐标转换为 3D 场景中的世界坐标,以便在 Three.js 等 3D 渲染引擎中使用。

说白了即使将地图坐标转换成three.js中的世界坐标的。

这里面提供了几个方法:

  • 坐标转换:通过 customCoords.lngLatsToCoords() 方法,可以将经纬度数组转换为 3D 场景中的坐标数组

  • 设置中心点customCoords.setCenter() 方法设置 3D 场景的中心点,用于确定 3D 模型的参考原点(世界坐标原点),通常在每次的render函数调用中执行一遍。

  • 获取相机参数customCoords.getCameraParams() 方法可以获取当前地图视角的相机参数(如视野范围、近平面、远平面等),这些参数可以用于同步 Three.js 的相机状态

避坑

1.加载失败,报错WebGL 1 is not supported since r163。

这个意思很明显,就是three.js的r163版本之后,不在支持WebGL 1了。此时再仔细看了一眼官网,发现其用的是r142版本,和官网严格对其,即可解决。

2.TS提示AMap.GLCustomLayer不存在

运行没问题,写代码时提示不存在,于是我去翻了翻types插件,发现其实是有的,只不过没有导出而已,自己手动更改一下就能解决

   =====>>>>  

但是,上面提到的AMap.Map.customCoords,我是真的没有找到,可能没有这types插件里面定义,因为官网也说了,只是定义了大部分,这个估计是个漏网之鱼。

3.中心点设置

在代码中可以看到,map的center设置的是三个Box中第二个的位置,此时添加一个AxesHelper,可以看到世界坐标的原点其实是第一个Box的位置。

因此地图的中心点与glLayer图层内3D模型世界坐标的中心点并不是一回事,是分开来单独设置的。three.js 的中心点就是通过customCoords.setCenter()设置的。

4.标记点不显示的问题

这里面并没有添加标记点,我是在一开始学的时候遇到了,这里一并记录。

我按照官网的教程,添加了一个标记点:

  const marker = new AMap.Marker({position: new AMap.LngLat(116.39, 39.9),title: "北京",offset: new AMap.Pixel(-10, -10),icon: "//vdata.amap.com/icons/b18/1/2.png", // 添加 icon 图标 URL});map.add(marker);

结果:

标记点理应是一个红星放在地图上,结果却没有显示。我查看了一下DOM结构,发现最外的盒子大小是0:

于是我修改了一下css:

// 保证marker能正常显示
:deep(.amap-markers) {width: 100%;height: 100%;
}

好了:

但是这样显然有点不合理,这么大一个工程怎么可能会有这种错误。我又向上翻了一翻,结果发现了一个关键的css,那就是overflow:hidden。

因为我这个工程是用来写three.js的,而默认情况下,three.js老爱出现滚动条,于是我在App.vue的根节点添加了overflow:hidden,所以导致后续的子节点全部自动继承,只需要将其注释掉,那么不添加上述的css字段也是ok的。

// APP.vue
// overflow: hidden;
// overflow-x: hidden;// index.vue
// // 保证marker能正常显示
// :deep(.amap-markers) {
//   width: 100%;
//   height: 100%;
// }

 这个纯属是我个人问题导致,记录一下自己的愚蠢。

http://www.xdnf.cn/news/1805.html

相关文章:

  • IMX6ULL 最新方案移植教程中间间系列5——向开发板迁移SSH和FTP
  • LeetCode hot 100—最长有效括号
  • 【FAQ】安装Agent的主机,为何不能更改显示分辨率
  • CVE-2025-32102 | Ubuntu 下复现 CrushFTP telnetSocket接口SSRF
  • dataType 和 content-type 参数的作用
  • 补4月22日23日
  • Sentieon软件发布V202503版本
  • 首版次软件产品有哪些阶段?专业软件测试服务公司分享
  • 使用String path = FileUtilTest.class.getResource(“/1.txt“).getPath(); 报找不到路径
  • Spring Boot 中配置线程池时优化 `ThreadPoolTaskExecutor` 的配置总结
  • DDL小练习
  • Java小公司实习面经
  • python字符串(3):字符集/编码(查看修改字符集,乱码);码点和字符的转换(chr和ord),字符串的编码解码函数(encode,decode)
  • Dockerfile指令
  • JavaScript 实现继承及 Class 本质详解
  • 【Python Web开发】02-Socket网络编程02
  • Java 高频面试题解析
  • Langchain提取结构化数据
  • 第九节:性能优化高频题-首屏加载优化策略
  • JS Array 方法 | 区分 slice 和 splice
  • `rfind()` 从字符串的右侧开始查找指定子字符串首次出现的位置
  • SiamFC算法深度解析
  • 深入浅出:Pinctrl与GPIO子系统详解
  • SpringCloud微服务架构设计与实践 - 面试实战
  • C语言别踩白块附源码
  • Trae+DeepSeek学习Python开发MVC框架程序笔记(四):使用sqlite存储查询并验证用户名和密码
  • 现代化个人博客系统 ModStartBlog v10.3.0 博客批量操作,博客评论智能审核,安全升级
  • 某大型电解铝厂电解系统谐波治理装置改造沃伦森电气
  • Linux之七大难命令(The Seven Difficult Commands of Linux)
  • U盘能识别但无法写入数据的原因