提示:以下是本篇文章正文内容,下面案例可供参考
一、使用步骤
1.引入库
代码如下(示例):
npm i @amap/amap-jsapi-loader
npm i three
根据需求自定义高度(结合自定义多边形)
2.读入数据
代码如下(示例):
<template>
<div>
高度<input type="text" v-model.number="height" />
<div class="myMap" ref="echart" id="myMap"></div>
</div>
</template>
<script>
import AMapLoader from "@amap/amap-jsapi-loader";
import * as THREE from "three";
export default {
name: "App",
data() {
return {
height: 1000,
};
},
mounted() {
this.initMap();
window.addEventListener("resize", this.onWindowResize);
},
methods: {
//加载地图api文件,修改自己高德地图key,版本不用调整,若调整请查看浏览器中会不会报错
initMap() {
AMapLoader.load({
key: "xxxxxx", //高德地图key,实际应用做到配置文件中
version: "2.0",
AMapUI: {
version: "1.1",
plugins: ["overlay/SimpleMarker"],
},
Loca: {
// 是否加载 Loca, 缺省不加载
version: "2.0.0",
},
plugins: [
"AMap.DistrictSearch",
"AMap.ToolBar",
"AMap.Polygon",
"AMap.PolygonEditor",
"AMap.MouseTool",
"AMap.ControlBar",
],
}).then(() => {
this.loadMap(); //实例化地图
});
},
// 初始化地图
loadMap() {
let _this = this;
let map = new AMap.Map("myMap", {
pitch: 45, //地图俯仰角度,有效范围 0 度- 83 度
zoom: 16,
// zooms: [9, 12], // 设置缩放范围
center: [116.396532, 39.918138],
viewMode: "3D", //地图模式
// dragEnable: false, // 禁止地图拖动
showLabel: true,
buildingAnimation: true,
// mapStyle: "amap://styles/darkblue",
// 工具栏默认包含缩放功能,确保它已启用
toolbar: {
visible: true,
},
showLabel: false, // 禁止显示文字标签
layers: [new AMap.TileLayer.Satellite()],
});
_this.myMap = map;
// 地图加载完成
_this.myMap.on("complete", () => {
_this.$nextTick(() => {
// 绘制多边形
_this.initLayer();
});
});
},
seve() {
this.initLayer();
},
initLayer() {
let _this = this;
const layer = new AMap.GLCustomLayer({
zIndex: 9999,
visible: true,
init: (gl) => {
_this.initThree(gl);
_this.createWall();
_this.animate();
},
render: () => {
const { near, far, fov, up, lookAt, position } =
_this.myMap.customCoords.getCameraParams();
this.camera.near = near; // 近平面
this.camera.far = far; // 远平面
this.camera.fov = fov; // 视野范围
this.camera.position.set(...position);
this.camera.up.set(...up);
this.camera.lookAt(...lookAt);
// 更新相机坐标系
this.camera.updateProjectionMatrix();
this.renderer.render(this.scene, this.camera);
// 这里必须执行!重新设置 three 的 gl 上下文状态
this.renderer.resetState();
},
});
this.myMap.add(layer);
},
initThree(gl) {
const container = document.getElementById("myMap");
this.camera = new THREE.PerspectiveCamera(
60,
container.clientWidth / container.clientHeight,
100,
1 << 30
);
this.renderer = new THREE.WebGLRenderer({
context: gl,
// canvas: document.querySelector('.amap-layer'), //也可以直接用canvas初始化
antialias: true, // 抗锯齿,默认false 耗性能
});
// 自动清空画布这里必须设置为 false,否则地图底图将无法显示
this.renderer.autoClear = false;
this.renderer.outputEncoding = THREE.sRGBEncoding;
this.scene = new THREE.Scene();
// 增加环境光
const aLight = new THREE.AmbientLight(0xffffff, 0.3);
this.scene.add(aLight);
},
createWall() {
const color = '#FFD500';
let faceList = [];
let faceVertexUvs = [];
// 墙体路径原始数据
const data = [
[
[116.391836, 39.922464],
[116.401749, 39.922925],
[116.401921, 39.913643],
[116.392179, 39.913182],
],
[
[116.407196, 39.918264],
[116.417753, 39.918528],
[116.417581, 39.908455],
],
];
// 起点坐标=结束坐标形成封闭图形
data.forEach((item) => {
item.push(item[0])
});
console.log(data,'data');
// 地理坐标转为three坐标系,不管用不用arr,都需要转换一个非空数组
// 否则customCoords没实例化api会报错
this.paths = this.myMap.customCoords.lngLatsToCoords(data);
// 合并多个闭合范围
for (let i = 0; i < this.paths.length; i++) {
const { face, uvs } = this.generateVecData(this.paths[i]);
faceList = [...faceList, ...face];
faceVertexUvs = [...faceVertexUvs, ...uvs];
}
// 背景层
const geometry = new THREE.BufferGeometry();
geometry.setAttribute(
"position",
new THREE.BufferAttribute(new Float32Array(faceList), 3)
);
geometry.setAttribute(
"uv",
new THREE.BufferAttribute(new Float32Array(faceVertexUvs), 2)
);
const material1 = new THREE.MeshBasicMaterial({
color: color,
side: THREE.DoubleSide,
transparent: true,
depthWrite: false,
// wireframe: true,
opacity: 0.5, // 添加这一行以设置透明度
depthTest: false,
});
const mesh1 = new THREE.Mesh(geometry, material1);
this.scene.add(mesh1);
// 动画层
const geometry2 = geometry.clone();
this.texture = this.generateTexture(128, color);
this.texture.wrapS = THREE.RepeatWrapping; // 水平重复平铺
this.texture.wrapT = THREE.RepeatWrapping; // 垂直重复平铺
const material2 = new THREE.MeshBasicMaterial({
side: THREE.DoubleSide,
transparent: true,
depthWrite: false,
map: this.texture,
depthTest: false,
});
const mesh2 = new THREE.Mesh(geometry2, material2);
this.scene.add(mesh2);
},
animate() {
let _this = this;
// 动效纹理偏移
let texture_offset = 0;
// 纹理偏移
texture_offset -= 0.03; // 向上移动
this.texture.offset.set(0, texture_offset);
if (this.myMap) {
this.myMap.render();
}
requestAnimationFrame(() => {
_this.animate();
});
},
generateVecData(arr) {
const vec3List = []; // 顶点数组
let faceList = []; // 三角面数组
let faceVertexUvs = []; // 面的UV层队列,用于纹理和几何信息映射
// t3---t2
// | \ |
// t0---t1
// UV面
// 下三角[t0, t1, t3]
// 上三角[t3, t1, t2]
const t0 = [0, 0];
const t1 = [1, 0];
const t2 = [1, 1];
const t3 = [0, 1];
// 高度
for (let i = 0; i < arr.length; i++) {
const [x1, y1] = arr[i];
vec3List.push([x1, y1, 0]);
vec3List.push([x1, y1, this.height]);
}
// 1---3
// | \ |
// 0---2
// 三角面顶点,没有顺序要求,但要跟UV面顺序一致
// 下三角 [0,1,2]
// 上三角 [1,2,3]
for (let i = 0; i < vec3List.length - 2; i++) {
if (i % 2 === 0) {
// 下三角
faceList = [
...faceList,
...vec3List[i],
...vec3List[i + 2],
...vec3List[i + 1],
];
// UV
faceVertexUvs = [...faceVertexUvs, ...t0, ...t1, ...t3];
} else {
// 上三角
faceList = [
...faceList,
...vec3List[i],
...vec3List[i + 1],
...vec3List[i + 2],
];
// UV
faceVertexUvs = [...faceVertexUvs, ...t3, ...t1, ...t2];
}
}
return {
face: faceList,
uvs: faceVertexUvs,
};
},
generateTexture(size = 64, color = "#ff0000") {
let canvas = document.createElement("canvas");
canvas.width = size;
canvas.height = size;
let ctx = canvas.getContext("2d");
let linearGradient = ctx.createLinearGradient(0, 0, 0, size);
linearGradient.addColorStop(0.2, this.hexToRgba(color, 0.0));
linearGradient.addColorStop(0.8, this.hexToRgba(color, 0.5));
linearGradient.addColorStop(1.0, this.hexToRgba(color, 1.0));
ctx.fillStyle = linearGradient;
ctx.fillRect(0, 0, size, size);
this.texture = new THREE.Texture(canvas);
this.texture.needsUpdate = true; //必须
return this.texture;
},
hexToRgba(hex, opacity = 1) {
return (
"rgba(" +
parseInt("0x" + hex.slice(1, 3)) +
"," +
parseInt("0x" + hex.slice(3, 5)) +
"," +
parseInt("0x" + hex.slice(5, 7)) +
"," +
opacity +
")"
);
},
},
};
</script>
<style lang="less">
#myMap {
height: 100vh;
}
</style>
高德官方有官方示例