给定一个矩形框用于规定地图打印范围,并截图打印该范围,用户可以在此范围内进行标绘,需要满足以下要求:
1)初始状态下,矩形框不随着地图的放大、缩小、移动而变化位置;(解锁状态)
2)点击锁定按钮后,矩形框要随着地图的放大、缩小、移动而变化位置;
3)锁定与解锁可以切换;
4)添加经纬网格,并标注该线的经纬度;
5)框的宽高比与A3、A4纸相同。
20230324-截图
一、绘制矩形框
1、初始状态——解锁状态——矩形框不随着地图的放大、缩小、移动而变化位置
思路:地图经纬度坐标与屏幕像素坐标转换。(这里给小白解释一下,屏幕像素坐标指的是在屏幕上像素点的坐标,原点需要自己验证具体位置,我的屏幕坐标原点是左上角,x轴是水平方向,y轴是垂直方向。想了解更多可以问问度娘)
1、1初始进入地图获取中心点,根据矩形宽高确定左上角、右下角的经纬度,利用leaflet绘制出矩形;
1、2将该经纬度转成屏幕像素坐标,保存起来;(这样无论怎么操作地图,矩形框在屏幕上的位置是定好的。)
1、3当我们操作地图[放大、缩小、移动]后,我们就把像素坐标转为经纬度坐标,再利用leaflet绘制出矩形。
//绘制矩形函数,需要时直接调用就行
drawRectangle(bounds) {
//判断矩形是否存在,存在就移除
if (this.layergroupPolygon) this.layergroup.removeLayer(this.layergroupPolygon)
//根据传来的bounds,重新绘制矩形
this.layergroupPolygon = L.rectangle(bounds, { weight: 2 }).addTo(this.layergroup);
//矩形框背景颜色透明度为0,也就是没有背景颜色。这一步个人取舍
this.layergroupPolygon.setStyle({
opacity: 1,
fillOpacity: 0,
})
},
//初始化的准备
getCenter() {
//是否为锁定状态
this.isLock = false
//获取当前页面的地图中心点
let center = this.bindMap.view.center
//定义距离中心点的上边距、左边距;方便得到矩形各个顶点的经纬度
//这个距离我是按照A4的比例定的,有需要A3A2的纸张比例的可以自己写一个下拉框,优化即可
let top = 15
let left = top * 1.414
//计算左上角、右下角的经纬度
let topx = center[0] - left
let topy = center[1] + top
let bottomx = center[0] + left
let bottomy = center[1] - top
//注意:leaflet的经纬度坐标是纬度在前,经度在后
this.top_latlng = [topy, topx]
this.bottom_latlng = [bottomy, bottomx]
//计算左上角、右下角的像素坐标——经纬度转像素坐标,可查看leaflet官网
this.screenCoor1 = this.bindMap.latLngToContainerPoint([topx, topy])
this.screenCoor2 = this.bindMap.latLngToContainerPoint([bottomx, bottomy])
//图层不存在时再创建
if (!this.layergroup) {
this.layergroup = L.layerGroup().addTo(this.bindMap.map);
}
//调用经纬度坐标,在地图上绘制矩形
let bounds = [this.top_latlng, this.bottom_latlng]
this.drawRectangle(bounds)
//监听地图变化,变化且未锁定时更新矩形
this.bindMap.map.on('move zoom', () => {
if (!this.isLock) this.update()
})
},
// 更新地理区域
update() {
//更新矩形时,要先利用 保存起来的屏幕坐标 转为 当前地图状态的经纬度 再画图
this.top_latlng = this.bindMap.map.containerPointToLatLng(this.screenCoor1)
this.bottom_latlng = this.bindMap.map.containerPointToLatLng(this.screenCoor2)
//修改矩形信息
this.layergroupPolygon.setBounds([this.top_latlng, this.bottom_latlng])
},
2、点击锁定按钮后,矩形框要随着地图的放大、缩小、移动而变化位置;
思路:点击锁定,保存当前经纬度不再变化,也就是不让像素坐标转为经纬度坐标。地图监听事件不再更新画矩形。
// 锁定框选
clockReact() {
//点击锁定,再次点击解锁
this.isLock = !this.isLock
this.update()
},
二、添加经纬网
经纬网代码可以根据自己需要修改间距、颜色等样式等。
// 是否添加经纬线
isAddLatlng() {
//根据isAdd 判断是否添加经纬线
this.isAdd = !this.isAdd
// 创建经纬线图层
if (!this.lonLatGridLineLayer)
this.lonLatGridLineLayer = L.featureGroup().addTo(this.bindMap.map);
if (this.isAdd) {
this.addLatlng()
} else {
this.lonLatGridLineLayer.clearLayers();
}
},
// 添加经纬线
addLatlng() {
if (!this.isShowReact) return this.$message.warning('请先选取范围')
// 添加经纬线
let addLonLatLine = () => {
let zoom = this.bindMap.map.getZoom();
let bounds = this.layergroupPolygon.getBounds();
let north = bounds.getNorth();
let east = bounds.getEast();
// 经纬度间隔
let d = 90 / Math.pow(2, zoom - 1);
// 经线网格
for (let index = -180; index <= 360; index += d) {
// 判断当前视野内
if (bounds.contains([north, index])) {
// 绘制经线
let lonLine = L.polyline(
[
[-90, index],
[90, index],
],
{ weight: 1, color: "blue" }
);
this.lonLatGridLineLayer.addLayer(lonLine);
// 标注
let text = index.toFixed(1) + "°";
// 动态计算小数位数
if (zoom > 10) {
text = index.toFixed((zoom - 8) / 2) + "°";
}
let divIcon = L.divIcon({
html: `<div style="white-space: nowrap;color:red;">${text}</div>`,
iconAnchor: [0, -5],
});
let textMarker = L.marker([north, index], { icon: divIcon });
this.lonLatGridLineLayer.addLayer(textMarker);
}
}
if (d > 90) d = 90;
// 纬线网格
for (let index = -90; index <= 90; index += d) {
if (bounds.contains([index, east])) {
let lonLine = L.polyline(
[
[index, -180],
[index, 360],
],
{ weight: 1, color: "blue" }
);
this.lonLatGridLineLayer.addLayer(lonLine);
// 标注
let text = index.toFixed(1) + "°";
if (zoom > 10) {
text = index.toFixed((zoom - 8) / 2) + "°";
}
let divIcon = L.divIcon({
html: `<div style="white-space: nowrap;color:red;">${text}</div>`,
iconAnchor: [(text.length + 1) * 6, 0],
});
let textMarker = L.marker([index, east], { icon: divIcon });
this.lonLatGridLineLayer.addLayer(textMarker);
}
}
}
addLonLatLine();
this.bindMap.map.on("zoomend move", () => {
this.lonLatGridLineLayer.clearLayers();
addLonLatLine();
});
},
三、截图打印
思路:把矩形框里面的东西截取下来
但是查遍插件都需要获取DOM元素,再进行截图。基于这个情况,我们可以使用插件获取地图的截图,再利用画布的裁剪功能裁出来我们想要的范围。
这里需要验证你的像素坐标位置是否正确。
下载插件:npm install dom-to-image
引用插件:import domtoimage from "dom-to-image"; 哪个页面使用放到哪个页面就可以
// 获取截图
getCutPic() {
//使用插件domtoimage
domtoimage
.toPng(this.bindMap.$el)
.then((dataUrl) => {
console.log(dataUrl);
this.imgUrl = dataUrl;
this.secondCut()
})
.catch(function (error) {
console.error("oops, something went wrong!", error);
});
},
//二次截图
async secondCut() {
//this.handleSrc是存放最终截图效果的位置
this.handleSrc = await this.handleImg({
src: this.imgUrl,
rect: {
x: this.screenCoor1.x, // 截取方格左上角横坐标
y: this.screenCoor1.y, // 截取方格左上角纵坐标
width: this.screenCoor2.x - this.screenCoor1.x, // 截取方格宽度
height: this.screenCoor2.y - this.screenCoor1.y // 截取方格高度
}
})
},
handleImg(opts) {
return new Promise((resolve, reject) => {
console.log(opts);
const { src, rect } = opts
if (!src || !rect) {
reject(new Error('opts params Error!'))
}
const img = new Image()
img.src = src
img.onload = function () {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
const { x, y, width, height } = rect
canvas.width = width
canvas.height = height
ctx.drawImage(this, x, y, width, height, 0, 0, width, height)
const url = canvas.toDataURL('image/png')
resolve(url)
}
img.onerror = function (err) {
reject(err)
}
})
},
至此,代码已经书写完毕,本人也是小白,有错误或者需要优化的可以随时联系我,希望我的文章对大家有帮助,谢谢!