实现
接着上篇文章写(https://blog.csdn.net/luoluoyang23/article/details/122653363),这次会完整的实现截图的功能
首先要放个地图,直接参照https://openlayers.org/en/latest/doc/quickstart.html写就行了
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.12.0/css/ol.css" type="text/css">
<title>Document</title>
<style type="text/css">
html, body {
margin: 0;
padding: 0;
}
#screenshot {
border: 2px solid black;
}
.map {
width: 100%;
height: 600px;
}
</style>
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.12.0/build/ol.js"></script>
</head>
<body>
<div id="map" class="map"></div>
</body>
<script>
const map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.fromLonLat([37.41, 8.82]),
zoom: 4
})
})
window.addEventListener("mousedown", (e) => {
const [startX, startY] = [e.clientX, e.clientY]
const divDom = document.createElement("div")
divDom.id = 'screenshot'
divDom.width = '1px'
divDom.height = '1px'
divDom.style.position = "absolute"
divDom.style.top = startY + "px"
divDom.style.left = startX + "px"
document.body.appendChild(divDom)
const moveEvent = (e) => {
const moveX = e.clientX - startX
const moveY = e.clientY - startY
if (moveX > 0) {
divDom.style.width = moveX + 'px'
} else {
divDom.style.width = -moveX + 'px'
divDom.style.left = e.clientX + 'px'
}
if (moveY > 0) {
divDom.style.height = moveY + 'px'
} else {
divDom.style.height = -moveY + 'px'
divDom.style.top = e.clientY + 'px'
}
}
window.addEventListener("mousemove", moveEvent)
window.addEventListener("mouseup", () => {
window.removeEventListener("mousemove", moveEvent)
})
})
</script>
</html>
我这里代码是直接在上次的代码基础上写的,看看效果
改点东西,因为我们截图事件是直接写在windows上面的,这样点地图也会触发,随便加个按钮,把事件挪过去
<button id="screen-button">截图</button>
...
document.getElementById('screen-button').addEventListener('click', (e) => {
const mousedownEvent = (e) => {
const [startX, startY] = [e.clientX, e.clientY]
const divDom = document.createElement("div")
divDom.id = 'screenshot'
divDom.width = '1px'
divDom.height = '1px'
divDom.style.position = "absolute"
divDom.style.top = startY + "px"
divDom.style.left = startX + "px"
document.body.appendChild(divDom)
const moveEvent = (e) => {
const moveX = e.clientX - startX
const moveY = e.clientY - startY
if (moveX > 0) {
divDom.style.width = moveX + 'px'
} else {
divDom.style.width = -moveX + 'px'
divDom.style.left = e.clientX + 'px'
}
if (moveY > 0) {
divDom.style.height = moveY + 'px'
} else {
divDom.style.height = -moveY + 'px'
divDom.style.top = e.clientY + 'px'
}
}
window.addEventListener("mousemove", moveEvent)
window.addEventListener("mouseup", () => {
window.removeEventListener("mousemove", moveEvent)
window.removeEventListener("mousedown", mousedownEvent)
})
}
window.addEventListener("mousedown", mousedownEvent)
})
这里直接移进去了,这样点击按钮后才会把截图事件绑定到window上,在鼠标松开时移除这些事件
现在我们在网页中测试能够发现,当我们截图时鼠标移动也会拖动地图移动,这样没办法很好的截图,解决这个问题有很多思路,可以在初始化地图的时候拿到拖动地图的事件进行控制,我之前在写地图色块提取的时候有用过透明遮罩的办法,自己权衡。这里采用前一种,参考:
https://blog.csdn.net/u013323965/article/details/53183532
let pan
map.getInteractions().forEach(function(element,index,array){
if(element instanceof ol.interaction.DragPan) pan = element;
})
在事件开始和结尾处加上相应的代码即可
然后就是获取图片了,在上一篇文章我已经给过相关API的链接地址,可以自己去研究,这里直接贴代码了
function getMapImg(startX, startY, mWidth, mHeight) {
map.once('rendercomplete', () => {
const mapCanvas = document.createElement('canvas')
mapCanvas.width = mWidth
mapCanvas.height = mHeight
const mapContext = mapCanvas.getContext('2d')
Array.prototype.forEach.call(
document.querySelectorAll('.ol-layer canvas'),
function (canvas) {
if (canvas.width > 0) {
const opacity = canvas.parentNode.style.opacity
mapContext.globalAlpha = opacity === '' ? 1 : Number(opacity)
const transform = canvas.style.transform
// Get the transform parameters from the style's transform matrix
const matrix = transform
.match(/^matrix\(([^(]*)\)$/)[1]
.split(',')
.map(Number)
// Apply the transform to the export map context
CanvasRenderingContext2D.prototype.setTransform.apply(
mapContext,
matrix
)
mapContext.drawImage(canvas, -startX, -startY)
}
}
)
if (navigator.msSaveBlob) {
// link download attribute does not work on MS browsers
navigator.msSaveBlob(mapCanvas.msToBlob(), 'map.png')
} else {
dataUrl = mapCanvas.toDataURL()
console.log(dataUrl)
}
})
map.renderSync()
}
四个参数,分别是截图范围的起点xy坐标,截图框的宽高。这就是我们写截图框代码的目的。这四个参数就从上面拿就行了,这里也直接写在之前的代码中获取,为了大家方便查看,这里直接贴出整体的代码
let [canvasX, canvasY] = [startX, startY]
let canvasWidth, canvasHeight
divDom.style.top = startY + "px"
divDom.style.left = startX + "px"
document.body.appendChild(divDom)
const moveEvent = (e) => {
const moveX = e.clientX - startX
const moveY = e.clientY - startY
if (moveX > 0) {
divDom.style.width = moveX + 'px'
canvasWidth = moveX
} else {
divDom.style.width = -moveX + 'px'
divDom.style.left = e.clientX + 'px'
canvasWidth = -moveX
canvasX = e.clientX
}
if (moveY > 0) {
divDom.style.height = moveY + 'px'
canvasHeight = moveY
} else {
divDom.style.height = -moveY + 'px'
divDom.style.top = e.clientY + 'px'
canvasHeight = -moveY
canvasY = e.clientY
}
}
在截图结束的地方调用getMapImg()
window.addEventListener("mouseup", () => {
window.removeEventListener("mousemove", moveEvent)
window.removeEventListener("mousedown", mousedownEvent)
pan.setActive(true)
getMapImg(canvasX, canvasY, canvasWidth, canvasHeight)
})
在getMapImg中最后几行的dataUrl就是图片地址,可以直接在浏览器中打开,现在我们测试效果
可以看到我们成功实现了效果,接下来就是一些优化截图表现效果的代码了。具体效果可以按照自己喜欢的来,我这里走比较常规的套路,在截图结束时清除截图框,在窗口中间增加一个预览截图的效果
这部分写得比较随意,下面时完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.12.0/css/ol.css" type="text/css">
<title>Document</title>
<style type="text/css">
html, body {
margin: 0;
padding: 0;
}
#screenshot {
border: 3px solid black;
}
.map {
width: 100%;
height: 600px;
}
#photo-window {
position: absolute;
border: 25px solid rgba(0, 0, 0, 0.5);
overflow: hidden;
}
.hide {
display: none;
}
</style>
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.12.0/build/ol.js"></script>
</head>
<body>
<div id="map" class="map"></div>
<button id="screen-button">截图</button>
<div id="photo-window" class="hide">
<img id="photo" src="" alt="#" />
</div>
</body>
<script>
const map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
view: new ol.View({
center: ol.proj.fromLonLat([37.41, 8.82]),
zoom: 4
})
})
let dataUrl
let pan
map.getInteractions().forEach(function(element,index,array){
if(element instanceof ol.interaction.DragPan) pan = element;
})
document.getElementById('screen-button').addEventListener('click', (e) => {
const mousedownEvent = (e) => {
pan.setActive(false)
const [startX, startY] = [e.clientX, e.clientY]
const divDom = document.createElement("div")
divDom.id = 'screenshot'
divDom.width = '1px'
divDom.height = '1px'
divDom.style.position = "absolute"
let [canvasX, canvasY] = [startX, startY]
let canvasWidth, canvasHeight
divDom.style.top = startY + "px"
divDom.style.left = startX + "px"
document.body.appendChild(divDom)
const moveEvent = (e) => {
const moveX = e.clientX - startX
const moveY = e.clientY - startY
if (moveX > 0) {
divDom.style.width = moveX + 'px'
canvasWidth = moveX
} else {
divDom.style.width = -moveX + 'px'
divDom.style.left = e.clientX + 'px'
canvasWidth = -moveX
canvasX = e.clientX
}
if (moveY > 0) {
divDom.style.height = moveY + 'px'
canvasHeight = moveY
} else {
divDom.style.height = -moveY + 'px'
divDom.style.top = e.clientY + 'px'
canvasHeight = -moveY
canvasY = e.clientY
}
}
window.addEventListener("mousemove", moveEvent)
window.addEventListener("mouseup", () => {
window.removeEventListener("mousemove", moveEvent)
window.removeEventListener("mousedown", mousedownEvent)
pan.setActive(true)
getMapImg(canvasX, canvasY, canvasWidth, canvasHeight)
document.body.removeChild(divDom)
generateWindow()
})
}
window.addEventListener("mousedown", mousedownEvent)
})
function getMapImg(startX, startY, mWidth, mHeight) {
map.once('rendercomplete', () => {
const mapCanvas = document.createElement('canvas')
mapCanvas.width = mWidth
mapCanvas.height = mHeight
const mapContext = mapCanvas.getContext('2d')
Array.prototype.forEach.call(
document.querySelectorAll('.ol-layer canvas'),
function (canvas) {
if (canvas.width > 0) {
const opacity = canvas.parentNode.style.opacity
mapContext.globalAlpha = opacity === '' ? 1 : Number(opacity)
const transform = canvas.style.transform
// Get the transform parameters from the style's transform matrix
const matrix = transform
.match(/^matrix\(([^(]*)\)$/)[1]
.split(',')
.map(Number)
// Apply the transform to the export map context
CanvasRenderingContext2D.prototype.setTransform.apply(
mapContext,
matrix
)
mapContext.drawImage(canvas, -startX, -startY)
}
}
)
if (navigator.msSaveBlob) {
// link download attribute does not work on MS browsers
navigator.msSaveBlob(mapCanvas.msToBlob(), 'map.png')
} else {
dataUrl = mapCanvas.toDataURL()
generateWindow(mWidth, mHeight)
}
})
map.renderSync()
}
function generateWindow(width, height) {
document.getElementById('photo').src = dataUrl
const boxDom = document.getElementById('photo-window')
boxDom.classList.remove('hide')
boxDom.style.left = 'calc(50% - ' + (width / 2) + 'px)'
boxDom.style.top = 'calc(50% - ' + (height / 2) + 'px)'
}
</script>
</html>