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>