[Vue3]高德地图结合ThreeJs

3,253 阅读1分钟

这里使用的是vue-cli创建的vue3项目

安装依赖

高德地图
npm i @amap/amap-jsapi-loader -s
ThreeJS (version:139)
npm i three -s

代码

在看Vue代码之前可以看看官网demo的写法,可以两份对比的看
自定义图层-GLCustomLayer 结合 THREE

下面是Vue的代码

<template>
  <div ref="mapRef" id="container"></div>
</template>

<script setup>
import * as AMapLoader from "@amap/amap-jsapi-loader";
import * as THREE from "three";
import {nextTick, onMounted, ref} from "vue";

/* refs */
// 地图dom
const mapRef = ref()

//地图中心点
const center = [121.407394, 37.424985]
// 地图对象
let map = null
// 高德地图类
let AMap = null
// 渲染器
let render = null
// 场景
let scene = null
// 相机
let camera = null
// 坐标转换器(经纬度转three位置)
let customCoords = null
// 转化后的中心点坐标
let comLocation = null

function initScene() {
  // 初始化场景
  scene = new THREE.Scene();
}

// 初始化光源
function initLight() {
  // 创建环境光
  let light = new THREE.AmbientLight(0xffffff, 1);
  light.position.set(0, 0, 0); //点光源位置
  scene.add(light);
}

// 初始化相机
function initCamera() {
  camera = new THREE.PerspectiveCamera(
      60,
      mapRef.value.offsetWidth / mapRef.value.offsetHeight,
      100,
      1 << 30
  );
}

// 添加盒子
function addBox() {
  let mat = new THREE.MeshLambertMaterial({
    side: THREE.DoubleSide,
    color: 0x1e2f97,
    transparent: true,
    opacity: .4,
    depthWrite: false
  });
  let geo = new THREE.BoxBufferGeometry(30, 30, 30);
  let mesh = new THREE.Mesh(geo, mat);
  mesh.position.set(comLocation[0], comLocation[1], 15);
  scene.add(mesh);
}

// 动画
function animate() {
  // 渲染时刷新地图
  map.render();
  // 自动刷新
  requestAnimationFrame(animate);
}

// 初始化树
function initThree() {
  // 初始化数据转换工具
  customCoords = map.customCoords;
  // 数据使用转换工具进行转换,这个操作必须要提前执行(在获取镜头参数 函数之前执行),否则将会获得一个错误信息。
  comLocation = customCoords.lngLatToCoord(center);
  // 创建webgl layer
  let glLayer = new AMap.GLCustomLayer({
    // 图层的层级 这里写的太大模型会消失
    zIndex: 10,
    // 初始化的操作,创建图层过程中执行一次。
    init: (gl) => {
      render = new THREE.WebGLRenderer({
        context: gl
      })
      // 自动清空画布这里必须设置为 false,否则地图底图将无法显示
      render.autoClear = false;

      initScene()
      initLight()
      initCamera()

      // 添加盒子 如果不延迟 或者延迟时间少了会保一个警告(Vertex buffer is not big enough for the draw call)
      setTimeout(() => {
        addBox()
      }, 500)
    },
    render: () => {
      render.state.reset()
      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();

      render.render(scene, camera);
    },
  })

  map.add(glLayer)
  animate()
}
// 初始化地图
function initMap() {
  AMapLoader.load({
    key: 'dae9f17b30c2fcce6c38fafc61c77f52',//首次load必填
    version: '2.0'
  }).then((_AMap) => {
    AMap = _AMap
    map = new AMap.Map('container', {
      viewMode: '3D', // 地图模式
      mapStyle: 'amap://styles/grey',
      pitch: 55,
      zoom: 18,
      center: center,
      // 只显示的特征
      features: ['bg', 'road', 'building']
    });
    initThree()
  }).catch((e) => {
    console.error(e);
  });
}

onMounted(() => {
  nextTick(() => {
    initMap()
  })
})

</script>

<style lang="scss">
html, body {
  margin: 0;
  padding: 0;
}

#container {
  height: 100vh;
}
</style>