Vue3项目使用高德地图生成沿途轨迹并通过htmltocanvas生成图片

Vue3项目使用高德地图生成沿途轨迹并通过htmltocanvas生成图片

一、引入高德地图

1、安装依赖包

css 复制代码
npm i @amap/amap-jsapi-loader --save

2、在高德地图开发者管理后台创建一个Web端(JS API)的应用,会有对应的key以及secret

main.js

javascript 复制代码
//高德的安全密钥
`window._AMapSecurityConfig = {
  securityJsCode: "你创建应用的secret",
};`

3、页面组件内创建一个div容器,用于展示地图

javascript 复制代码
<div id="container"></div>

4、在页面加载完创建地图

javascript 复制代码
import { onMounted } from "vue";
import AMapLoader from "@amap/amap-jsapi-loader";

let map = null;
onMounted(() => {
  loadMap();
});
const loadMap = () => {
  AMapLoader.load({
    key: "申请好的Web端开发者Key", // 申请好的Web端开发者Key,首次调用 load 时必填
    version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
    plugins: ["AMap.ToolBar", "AMap.Scale", "AMap.Driving"], //需要使用的的插件列表,如比例尺'AMap.Scale',支持添加多个如:['...','...']
  })
    .then((AMap) => {
      console.log({ AMap });
      map = new AMap.Map("container", {
        // 设置地图容器id
        viewMode: "3D", // 是否为3D地图模式
        mapStyle: "amap://styles/blue", //设置地图的显示样式
        zoom: 11, // 初始化地图级别
        center: [116.397428, 39.90923], // 初始化地图中心点位置
      });
      map.addControl(new AMap.ToolBar());
      // var map = new AMap.Map("container", {
      //   center: [116.397559, 39.89621],
      //   zoom: 14,
      // });

      var drivingOption = {
        // policy: AMap.DrivingPolicy?.LEAST_TIME, // 其它policy参数请参考 https://lbs.amap.com/api/javascript-api/reference/route-search#m_DrivingPolicy
        ferry: 1, // 是否可以使用轮渡
        province: "京", // 车牌省份的汉字缩写
      };

      // 构造路线导航类
      var driving = new AMap.Driving(drivingOption);

      // 根据起终点经纬度规划驾车导航路线
      driving.search(
        new AMap.LngLat(116.379028, 39.865042),
        new AMap.LngLat(113.93669599999998, 22.532742),
        {
          waypoints: [new AMap.LngLat(102.71530800000005, 25.040639000000002)],
        },
        function (status, result) {
          // result即是对应的驾车导航信息,相关数据结构文档请参考 https://lbs.amap.com/api/javascript-api/reference/route-search#m_DrivingResult
          if (status === "complete") {
            if (result.routes && result.routes.length) {
              // 绘制第一条路线,也可以按需求绘制其它几条路线
              drawRoute(result.routes[0]);
              // log.success("绘制驾车路线完成");
            }
          } else {
            // log.error("获取驾车数据失败:" + result);
          }
        }
      );
      // var map = new AMap.Map("container", {
      //   viewMode: "3D",
      //   zoom: 13,
      //   center: [116.4, 39.92],
      //   resizeEnable: true,
      // });
      var marks = [
        {
          text: "北京",
          position: [116.379028, 39.865042],
        },
        {
          text: "河北",
          position: [114.54945499999997, 38.02025800000003],
        },
        {
          text: "山西",
          position: [112.56996700000002, 37.865196],
        },
        {
          text: "陕西",
          position: [108.94587200000001, 34.247232000000004],
        },
        {
          text: "四川",
          position: [104.07254, 30.675341],
        },
        {
          text: "云南",
          position: [102.71530800000005, 25.040639000000002],
        },
        {
          text: "广西",
          position: [108.31413899999995, 22.807084000000003],
        },
        {
          text: "广东",
          position: [113.93669599999998, 22.532742],
        },
      ];
      for (let index = 0; index < marks.length; index++) {
        // 创建纯文本标记
        let mark = marks[index];
        let text = new AMap.Text({
          text: mark.text,
          anchor: "center", // 设置文本标记锚点
          draggable: true,
          cursor: "pointer",
          angle: 0,
          style: {
            padding: ".15rem 0.25rem",
            "margin-bottom": "1rem",
            "border-radius": ".25rem",
            "background-color": "blue",
            "border-width": 0,
            "box-shadow": "0 2px 6px 0 rgba(114, 124, 245, .5)",
            "text-align": "center",
            "font-size": "10px",
            color: "white",
          },
          position: mark.position,
        });

        text.setMap(map);
      }

      function drawRoute(route) {
        var path = parseRouteToPath(route);

        var startMarker = new AMap.Marker({
          position: path[0],
          icon: "https://webapi.amap.com/theme/v1.3/markers/n/start.png",
          map: map,
        });

        var endMarker = new AMap.Marker({
          position: path[path.length - 1],
          icon: "https://webapi.amap.com/theme/v1.3/markers/n/end.png",
          map: map,
        });

        var routeLine = new AMap.Polyline({
          path: path,
          isOutline: true,
          outlineColor: "#ffeeee",
          borderWeight: 2,
          strokeWeight: 5,
          strokeOpacity: 0.9,
          strokeColor: "#0091ff",
          lineJoin: "round",
        });

        map.add(routeLine);

        // 调整视野达到最佳显示区域
        map.setFitView([startMarker, endMarker, routeLine]);
      }

      // 解析DrivingRoute对象,构造成AMap.Polyline的path参数需要的格式
      // DrivingResult对象结构参考文档 https://lbs.amap.com/api/javascript-api/reference/route-search#m_DriveRoute
      function parseRouteToPath(route) {
        var path = [];

        for (var i = 0, l = route.steps.length; i < l; i++) {
          var step = route.steps[i];

          for (var j = 0, n = step.path.length; j < n; j++) {
            path.push(step.path[j]);
          }
        }

        return path;
      }
    })
    .catch((e) => {
      console.log({ e });
    })
    .finally(() => {
      console.log("finally");
    });
};

5、展示效果

二、轨迹图常用于分享,使用htmltocanvas将轨迹图转换为图片,便于进行分享操作

1、安装htmltocanvas

js 复制代码
npm install html2canvas --save

2、htmltocanvas的特殊处理,至关重要

main.js

js 复制代码
// 解决导出高德地图可能出现空白
HTMLCanvasElement.prototype.getContext = (function (origFn) {
  return function (type, attributes) {
    if (type === "webgl") {
      attributes = Object.assign({}, attributes, {
        preserveDrawingBuffer: true,
      });
    }
    return origFn.call(this, type, attributes);
  };
})(HTMLCanvasElement.prototype.getContext);

3、触发截图操作及生成图片展示

html 复制代码
<div @click="fallbackToHtml2Canvas()">截图</div>
<img :src="imgData" alt="" srcset="" style="width: 100%; height: 400px" />
js 复制代码
import { onMounted, ref } from "vue";
import html2canvas from "html2canvas";

const imgData = ref("");
const fallbackToHtml2Canvas = async () => {
  try {
    const container = document.getElementById("container");

    // 等待地图渲染完成
    await new Promise((resolve) => {
      // 等待一段时间确保所有图层加载完成
      setTimeout(resolve, 1000);
    });

    // 配置 html2canvas 选项以更好地处理地图
    const canvas = await html2canvas(container, {
      useCORS: true, // 处理跨域图片
      allowTaint: true, // 允许跨域内容(可能会有安全限制)
      backgroundColor: null, // 设置背景色
      scale: 1, // 保持原始比例
      logging: true, // 启用日志
      removeContainer: true, // 完成后移除临时容器
      foreignObjectRendering: false, // 不使用 foreignObject 渲染---至关重要
      imageTimeout: 15000, // 增加图片加载超时时间
      // ignoreElements: (element) => {
      //   // 忽略一些不需要截图的元素
      //   return (
      //     element.classList &&
      //     (element.classList.contains("amap-logo") ||
      //       element.classList.contains("amap-copyright"))
      //   );
      // },
    });

    const image = canvas.toDataURL("image/png");
    imgData.value = image;
    console.log("使用 html2canvas 截图完成");
    return true;
  } catch (error) {
    console.error("html2canvas 截图失败:", error);
    imgData.value = ""; // 清空图片数据
    return false;
  }
};

3、截图效果

至此已全部完成

三、最终完整代码

1、main.js

typescript 复制代码
import { createApp } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";
import router from "./router";
import "./assets/main.css";
const app = createApp(App);

//高德的安全密钥
window._AMapSecurityConfig = {
  securityJsCode: "你创建的应用的安全密匙",
};
// 解决导出高德地图可能出现空白
HTMLCanvasElement.prototype.getContext = (function (origFn) {
  return function (type, attributes) {
    if (type === "webgl") {
      attributes = Object.assign({}, attributes, {
        preserveDrawingBuffer: true,
      });
    }
    return origFn.call(this, type, attributes);
  };
})(HTMLCanvasElement.prototype.getContext);

app.use(createPinia());
app.use(router);

app.mount("#app");

2、组件

xml 复制代码
<template>
  <div class="about">
    <div id="container"></div>
    <div @click="fallbackToHtml2Canvas()">截图</div>
    <img :src="imgData" alt="" srcset="" style="width: 100%; height: 400px" />
  </div>
</template>
<script setup>
import { onMounted, ref } from "vue";
import html2canvas from "html2canvas";
import AMapLoader from "@amap/amap-jsapi-loader";
let map = null;
// eslint-disable-next-line no-unused-vars
const imgData = ref("");
const fallbackToHtml2Canvas = async () => {
  try {
    const container = document.getElementById("container");

    // 等待地图渲染完成
    await new Promise((resolve) => {
      // 等待一段时间确保所有图层加载完成
      setTimeout(resolve, 1000);
    });

    // 配置 html2canvas 选项以更好地处理地图
    const canvas = await html2canvas(container, {
      useCORS: true, // 处理跨域图片
      allowTaint: true, // 允许跨域内容(可能会有安全限制)
      backgroundColor: null, // 设置背景色
      scale: 1, // 保持原始比例
      logging: true, // 启用日志
      removeContainer: true, // 完成后移除临时容器
      foreignObjectRendering: false, // 不使用 foreignObject 渲染
      imageTimeout: 15000, // 增加图片加载超时时间
      // ignoreElements: (element) => {
      //   // 忽略一些不需要截图的元素
      //   return (
      //     element.classList &&
      //     (element.classList.contains("amap-logo") ||
      //       element.classList.contains("amap-copyright"))
      //   );
      // },
    });

    const image = canvas.toDataURL("image/png");
    imgData.value = image;
    console.log("使用 html2canvas 截图完成");
    return true;
  } catch (error) {
    console.error("html2canvas 截图失败:", error);
    imgData.value = ""; // 清空图片数据
    return false;
  }
};
onMounted(() => {
  loadMap();
});
const loadMap = () => {
  AMapLoader.load({
    key: "c2d8624cfd8ff1058ab6e3ea23d9a779", // 申请好的Web端开发者Key,首次调用 load 时必填
    version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
    plugins: ["AMap.ToolBar", "AMap.Scale", "AMap.Driving"], //需要使用的的插件列表,如比例尺'AMap.Scale',支持添加多个如:['...','...']
  })
    .then((AMap) => {
      console.log({ AMap });
      map = new AMap.Map("container", {
        // 设置地图容器id
        viewMode: "3D", // 是否为3D地图模式
        mapStyle: "amap://styles/blue", //设置地图的显示样式
        zoom: 11, // 初始化地图级别
        center: [116.397428, 39.90923], // 初始化地图中心点位置
      });
      map.addControl(new AMap.ToolBar());
      // var map = new AMap.Map("container", {
      //   center: [116.397559, 39.89621],
      //   zoom: 14,
      // });

      var drivingOption = {
        // policy: AMap.DrivingPolicy?.LEAST_TIME, // 其它policy参数请参考 https://lbs.amap.com/api/javascript-api/reference/route-search#m_DrivingPolicy
        ferry: 1, // 是否可以使用轮渡
        province: "京", // 车牌省份的汉字缩写
      };

      // 构造路线导航类
      var driving = new AMap.Driving(drivingOption);

      // 根据起终点经纬度规划驾车导航路线
      driving.search(
        new AMap.LngLat(116.379028, 39.865042),
        new AMap.LngLat(113.93669599999998, 22.532742),
        {
          waypoints: [new AMap.LngLat(102.71530800000005, 25.040639000000002)],
        },
        function (status, result) {
          // result即是对应的驾车导航信息,相关数据结构文档请参考 https://lbs.amap.com/api/javascript-api/reference/route-search#m_DrivingResult
          if (status === "complete") {
            if (result.routes && result.routes.length) {
              // 绘制第一条路线,也可以按需求绘制其它几条路线
              drawRoute(result.routes[0]);
              // log.success("绘制驾车路线完成");
            }
          } else {
            // log.error("获取驾车数据失败:" + result);
          }
        }
      );
      // var map = new AMap.Map("container", {
      //   viewMode: "3D",
      //   zoom: 13,
      //   center: [116.4, 39.92],
      //   resizeEnable: true,
      // });
      var marks = [
        {
          text: "北京",
          position: [116.379028, 39.865042],
        },
        {
          text: "河北",
          position: [114.54945499999997, 38.02025800000003],
        },
        {
          text: "山西",
          position: [112.56996700000002, 37.865196],
        },
        {
          text: "陕西",
          position: [108.94587200000001, 34.247232000000004],
        },
        {
          text: "四川",
          position: [104.07254, 30.675341],
        },
        {
          text: "云南",
          position: [102.71530800000005, 25.040639000000002],
        },
        {
          text: "广西",
          position: [108.31413899999995, 22.807084000000003],
        },
        {
          text: "广东",
          position: [113.93669599999998, 22.532742],
        },
      ];
      for (let index = 0; index < marks.length; index++) {
        // 创建纯文本标记
        let mark = marks[index];
        let text = new AMap.Text({
          text: mark.text,
          anchor: "center", // 设置文本标记锚点
          draggable: true,
          cursor: "pointer",
          angle: 0,
          style: {
            padding: ".15rem 0.25rem",
            "margin-bottom": "1rem",
            "border-radius": ".25rem",
            "background-color": "blue",
            "border-width": 0,
            "box-shadow": "0 2px 6px 0 rgba(114, 124, 245, .5)",
            "text-align": "center",
            "font-size": "10px",
            color: "white",
          },
          position: mark.position,
        });

        text.setMap(map);
      }

      function drawRoute(route) {
        var path = parseRouteToPath(route);

        var startMarker = new AMap.Marker({
          position: path[0],
          icon: "https://webapi.amap.com/theme/v1.3/markers/n/start.png",
          map: map,
        });

        var endMarker = new AMap.Marker({
          position: path[path.length - 1],
          icon: "https://webapi.amap.com/theme/v1.3/markers/n/end.png",
          map: map,
        });

        var routeLine = new AMap.Polyline({
          path: path,
          isOutline: true,
          outlineColor: "#ffeeee",
          borderWeight: 2,
          strokeWeight: 5,
          strokeOpacity: 0.9,
          strokeColor: "#0091ff",
          lineJoin: "round",
        });

        map.add(routeLine);

        // 调整视野达到最佳显示区域
        map.setFitView([startMarker, endMarker, routeLine]);
      }

      // 解析DrivingRoute对象,构造成AMap.Polyline的path参数需要的格式
      // DrivingResult对象结构参考文档 https://lbs.amap.com/api/javascript-api/reference/route-search#m_DriveRoute
      function parseRouteToPath(route) {
        var path = [];

        for (var i = 0, l = route.steps.length; i < l; i++) {
          var step = route.steps[i];

          for (var j = 0, n = step.path.length; j < n; j++) {
            path.push(step.path[j]);
          }
        }

        return path;
      }
    })
    .catch((e) => {
      console.log({ e });
    })
    .finally(() => {
      console.log("finally");
    });
};
</script>

<style>
#container {
  width: 100%;
  height: 400px;
}
.map {
  font-size: 20px;
}
.about {
  text-align: center;
}

@media (min-width: 1024px) {
  .about {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
  }
}
.amap-icon img {
  position: static;
}
</style>
相关推荐
chxii12 分钟前
2.9 插槽
前端·javascript·vue.js
接着奏乐接着舞。2 小时前
如何在Vue中使用拓扑图功能
前端·javascript·vue.js
老华带你飞2 小时前
生产管理ERP系统|物联及生产管理ERP系统|基于SprinBoot+vue的制造装备物联及生产管理ERP系统设计与实现(源码+数据库+文档)
java·数据库·vue.js·论文·制造·毕设·生产管理erp系统
阳先森2 小时前
Vue3 Proxy 为何不直接返回target[key],选用Reflect
前端·vue.js
水冗水孚4 小时前
使用useSearchParams或router.replace拼接地址栏——解决tab标签页刷新状态丢失问题
vue.js·react.js
阳先森4 小时前
vue 数据更新到视图变化全过程
前端·vue.js
Sobeit4 小时前
Vue 3.5 响应式设计与实现流程全解析
前端·vue.js
Live&&learn4 小时前
seo-使用nuxt定义页面标题和meta等信息
vue.js·性能优化
草履虫建模4 小时前
RuoYi OpenAPI集成从单体到微服务改造全过程记录
java·运维·vue.js·spring cloud·微服务·云原生·架构